# Description
Luogu传送门
vjudge传送门
不得不说,洛谷的翻译是真的迷,这里再给出一个翻译:
给你一个长度为 的数列,让你把这个数列划分成 段。
定义一段区间的价值为区间内数字两两异或的和,即 。
求一种划分方案使得 段价值和最小,只需要输出价值和。
数据范围:
# Solution
首先考虑朴素的 ,定义 表示前 个数,划分成 段的最小价值。
转移方程:
也就是前 个数划分成 段, 放到一段里,这一段的价值就是 。
可以 预处理出来,然后转移也是 的,总复杂度 , 无法通过此题。
重点来了,我们要使用四边形不等式优化 dp。
关于四边形不等式优化 dp 的详细讲解请大家左转百度一下吧 QwQ
我们知道,四边形不等式需要满足如下条件:
同时,我们知道只需要证明 即可证明上述条件。
所以我们来推导一下:
所以我们的转移方程满足四边形不等式。那么这个性质有什么用呢?
众所周知,拥有了这个性质之后我们就有了决策单调性,也就是 。
是转移到 时的最优决策点,就是上面转移方程里使得 取最优值时转移过来的那个 。
所以我们在对 进行转移时只需要枚举 即可。
# Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using namespace std;
const int N = 5010;
const ll INF = 1e18;
int n, m;
int a[N], p[N][N];
ll f[N][N], s[N][N];
inline ll sum(int x, int y){
return s[y][y] - s[x - 1][y] - s[y][x - 1] + s[x - 1][x - 1];
}
int main(){
scanf("%d%d", &n, &m); m++;
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
s[i][j] = (a[i] ^ a[j]) + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
for(int i = 0; i <= n; ++i)
for(int j = 0; j <= m; ++j) f[i][j] = INF;
f[0][0] = 0;
for(int j = 1; j <= m; ++j){
p[n + 1][j] = n;
for(int i = n; i >= 1; --i)
for(int k = p[i][j - 1]; k <= p[i + 1][j]; ++k)
if(f[i][j] > f[k][j - 1] + sum(k + 1, i))
f[i][j] = f[k][j - 1] + sum(k + 1, i), p[i][j] = k;
}
printf("%lld\n", f[n][m] >> 1);
return 0;
}