CF1456E XOR-ranges

lalaouye發表於2024-11-24

首先發現這種上下界再結合資料範圍,顯然是讓我們把一個數拆成個數不大於 \(2k\) 的段,其中每個段由一段字首和一段自由段組成。

考慮從高位到低位確定答案。把每個數拆位列出來,發現如果使用字首 dp 考慮前一個與後一個的答案沒用什麼進展。而我們如果把相鄰的三個列出來,發現如果中間數這個有限制的段最短的話,新的答案貢獻只與外面兩個數有關。我們發現這種情況是可以擴充歸納的,對於最小化代價我們可以考慮區間 dp,對於一個區間我們關心它的答案,並且我們需要欽定區間左邊的位置和右邊的位置都還存在限制,那麼對於當前左右兩邊的狀態我們需要知道他是貼著 \(l\) 的還是貼著 \(r\) 的,並且最後一個限制的位置是否產生了變化,具體的,設 \(f(i,l,r,p1,p2,q1,q2)\) 表示區間 \([l,r]\) 中的數在前 \(i\) 位已經解除了限制,而 \(l-1\) 的數和 \(r+1\) 的數的限制保持到了第 \(i\) 位,且這兩個數在第 \(i\) 位上的數是否產生了變化。

那麼如果 \([l,r]\) 在第 \(i\) 位前就已經都解除了限制,那麼該段在第 \(i\) 位產生貢獻當且僅當 \(l-1\) 上的數和 \(r+1\) 上的數在第 \(i\) 位不同。然後我們有轉移:

\[f(i,l,r,p1,p2,q1,q2)\leftarrow f(i+1,l,r,p1,0,q1,0)+val(l-1,r+1,i) \]

而如果區間 \([l,r]\) 存在第 \(i\) 位才解除的數,我們有轉移:

\[f(i,l,r,p1,p2,q1,q2)\leftarrow f(i,l,k-1,p1,p2,o,1)+f(i,k+1,r,o,1,q1,q2) \]

然後有一些細節,比如說需要保證該轉移合法,並且有的數到最後都沒有解除限制,這些都是要考慮的。

時間複雜度 \(\mathcal{O}(n^3k)\),程式碼:

#include <bits/stdc++.h>
#define int long long
#define ls (p << 1)
#define rs (ls | 1)
#define rep(i, l, r) for (int i = l; i <= r; ++ i)
#define rrp(i, l, r) for (int i = r; i >= l; -- i)
#define pii pair <int, int>
#define eb emplace_back
#define x first
#define y second
#define inf 1000000000
#define linf 1000000000000000000
using namespace std;
typedef long long ll;
constexpr int N = 50 + 5, P = 998244353;
inline int rd () {
	int x = 0, f = 1;
	char ch = getchar ();
	while (! isdigit (ch)) { if (ch == '-') f = -1; ch = getchar (); }
	while (isdigit (ch)) { x = (x << 1) + (x << 3) + ch - 48; ch = getchar (); }
	return x * f;
}
int n, K;
int L[N], R[N], a[N];
int f[N][N][N][2][2][2][2];
void chk (int &x, int y) {
  x = min (x, y);
}
int dfs (int c, int l, int r, int p1, int p2, int q1, int q2) {
  if (c == K) return l > r ? 0 : 1e18;
  int &ret = f[c][l][r][p1][p2][q1][q2];
  if (~ ret) return ret;
  ret = 1e18;
  int vl = ((p1 ? R[l - 1] : L[l - 1]) >> c) ^ p2;
  int vr = ((q1 ? R[r + 1] : L[r + 1]) >> c) ^ q2;
  chk (ret, dfs (c + 1, l, r, p1, 0, q1, 0) + a[c] * (l != 1 && r != n && ((vl ^ vr) & 1)));
  rep (k, l, r) {
    rep (o, 0, 1) {
      if (! c) chk (ret, dfs (c, l, k - 1, p1, p2, o, 0) + dfs (c, k + 1, r, o, 0, q1, q2));
      int w = (o ? R[k] : L[k]) ^ (1ll << c);
      if (L[k] <= (w & (~ ((1ll << c) - 1))) && (w | ((1ll << c) - 1)) <= R[k])
      chk (ret, dfs (c, l, k - 1, p1, p2, o, 1) + dfs (c, k + 1, r, o, 1, q1, q2));
    }
  }
  return ret;
}
int32_t main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  memset (f, -1, sizeof f);
  n = rd (), K = rd ();
  rep (i, 1, n) L[i] = rd (), R[i] = rd ();
  rep (i, 0, K - 1) a[i] = rd ();
  cout << dfs (0, 1, n, 0, 0, 0, 0);
}