JZOJ 6974. 【2021.02.01冬令营模拟】联邦解体
题目大意
- 长为
n
n
n的原序列中每个数有两个权值
A
i
,
B
i
A_i,B_i
Ai,Bi,求保持相对顺序不变的前提下,把原序列分割为两个子序列后所有相邻两项的
A
,
B
A,B
A,B权值之差的绝对值之和的最大值。
-
n
≤
1
0
6
nle10^6
n≤106
题解
- 既有绝对值,又要求最大值,考虑到若不取绝对值的话,一定不会比真实答案更优,所以可以去掉绝对值号,而只需保证相邻两项的
A
A
A或
B
B
B对答案的贡献符号相反,注意这里的贡献并不是每个数只贡献一次,而是与其左边和右边的数分别贡献一次,当且仅当边界处才贡献一次。
- 可以考虑DP,不需要记录两个序列中当前最后一个数分别是什么,只需记两个序列的最后一个数的
A
,
B
A,B
A,B分别取了什么符号,共
2
∗
2
∗
2
∗
2
=
16
2*2*2*2=16
2∗2∗2∗2=16种可能,但因为左右边界都少贡献一次,所以还要设两种状态表示是否为左右边界,所以状态一共为
6
∗
6
=
36
6*6=36
6∗6=36种。
- 转移的时候,先枚举上一位的状态,给当前的
A
i
,
B
i
A_i,B_i
Ai,Bi分别加上与上一位相反的贡献,再枚举当前位的贡献,再加上即可。对于左右边界的特殊情况,只需要简单的特判。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000010
#define ll long long
ll f[N][6][6], a[N], b[N];
int read() {
int s = 0;
char x = getchar();
while(x < '0' || x > '9') x = getchar();
while(x >= '0' && x <= '9') s = s * 10 + x - 48, x = getchar();
return s;
}
int main() {
int n = read(), i, j;
for(i = 1; i <= n; i++) a[i] = read(), b[i] = read();
memset(f, 128, sizeof(f));
f[0][4][4] = 0;
for(i = 1; i <= n; i++) {
for(j = 0; j < 6; j++) {
ll s0 = f[i - 1][0][j], s1 = f[i - 1][1][j], s2 = f[i - 1][2][j], s3 = f[i - 1][3][j];
s0 += a[i] + b[i], s1 += a[i] - b[i], s2 += b[i] - a[i], s3 -= a[i] + b[i];
ll s4 = f[i - 1][4][j];
ll s = max(s4, max(max(s0, s1), max(s2, s3)));
f[i][0][j] = max(f[i][0][j], s - a[i] - b[i]);
f[i][1][j] = max(f[i][1][j], s - a[i] + b[i]);
f[i][2][j] = max(f[i][2][j], s + a[i] - b[i]);
f[i][3][j] = max(f[i][3][j], s + a[i] + b[i]);
f[i][5][j] = max(f[i][5][j], s);
}
for(j = 0; j < 6; j++) {
ll s0 = f[i - 1][j][0], s1 = f[i - 1][j][1], s2 = f[i - 1][j][2], s3 = f[i - 1][j][3];
s0 += a[i] + b[i], s1 += a[i] - b[i], s2 += b[i] - a[i], s3 -= a[i] + b[i];
ll s4 = f[i - 1][j][4];
ll s = max(s4, max(max(s0, s1), max(s2, s3)));
f[i][j][0] = max(f[i][j][0], s - a[i] - b[i]);
f[i][j][1] = max(f[i][j][1], s - a[i] + b[i]);
f[i][j][2] = max(f[i][j][2], s + a[i] - b[i]);
f[i][j][3] = max(f[i][j][3], s + a[i] + b[i]);
f[i][j][5] = max(f[i][j][5], s);
}
}
printf("%lld
", f[n][5][5]);
return 0;
}
自我小结
- 拆绝对值已经不是第一次出现,套路很常见,要引起注意。