【题解】2021提高组十连测day4 n^3过一万

题目链接(正睿)

题目大意

给定含有\(n\)个元素的数组\(a\),现在要选择两个相邻的元素\((i,j)\)删除,那么代价为\(cost(i,j)\)(给出)。

\(\frac{n}{2}\)轮将所有元素删完,问代价最少是多少。\(n\le 4000\)

分析

二分答案,考虑使用区间\(\text{dp}\)\(dp(l,r)\)表示区间\([l,r]\)在枚举的最小代价下可不可行。如果\(dp(l,r)\)可行,\(dp(l-1,k)\)可行,那么\(dp(l,k)\)可行。在转移的时候,让\(dp(l,i)|=dp(r+1,i)\)即可。时间复杂度是\(O(n^3 \log n)\),使用\(\text{bitset}\)优化,时间复杂度\(O(n^3/w\log n)\),其中\(w\)约为\(64\)

实现

先二分枚举答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main() {
int L = 1e8, R = -1e8;
n = read();
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j += 2) {
a[i][j] = read();
R = max(R, a[i][j]);
L = min(L, a[i][j]);
}
}
while (L <= R) {
int mid = (L + R) >> 1;
if (check(mid)) R = mid - 1;
else L = mid + 1;
}
cout << L;
return 0;
}

核心代码,\(check\)函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bitset<N> f[N];

inline bool check(int x) {
for (int i = 1; i <= n; i++) {
f[i].reset();
if (a[i][i + 1] <= x) {
f[i][i + 1] = true;
}
}
for (int i = n; i >= 1; i--) {
for (int j = i + 1; j <= n; j += 2) {
if (f[i][j]) {
f[i] |= f[j + 1];
}
}
for (int j = i + 1; j <= n; j += 2) {
if (f[i + 1][j - 1] && a[i][j] <= x) {
f[i][j] = true;
f[i] |= f[j + 1];
}
}
}
return f[1][n];
}