JZOJ 6974. 【2021.02.01冬令营模拟】联邦解体(拆绝对值+DP)

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 n\le10^6 n106

题解

  • 既有绝对值,又要求最大值,考虑到若不取绝对值的话,一定不会比真实答案更优,所以可以去掉绝对值号,而只需保证相邻两项的 A A A B B B对答案的贡献符号相反,注意这里的贡献并不是每个数只贡献一次,而是与其左边和右边的数分别贡献一次,当且仅当边界处才贡献一次。
  • 可以考虑DP,不需要记录两个序列中当前最后一个数分别是什么,只需记两个序列的最后一个数的 A , B A,B A,B分别取了什么符号,共 2 ∗ 2 ∗ 2 ∗ 2 = 16 2*2*2*2=16 2222=16种可能,但因为左右边界都少贡献一次,所以还要设两种状态表示是否为左右边界,所以状态一共为 6 ∗ 6 = 36 6*6=36 66=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\n", f[n][5][5]);
	return 0;
}

自我小结

  • 拆绝对值已经不是第一次出现,套路很常见,要引起注意。
posted @ 2021-02-04 08:30  AnAn_119  阅读(95)  评论(0编辑  收藏  举报