题解:CF1421E Swedish Heroes

题意:很简单了,不再赘述。

做法:

首先可以很容易想到 \(O(n^3)\) 的区间 dp,然而跟正解没什么关系。

我们考虑每个数对最后答案的贡献,系数肯定是 \(+1/-1\),那么我们考虑一个 \(+1,-1\) 序列什么时候可以合法,经过精湛的打表技术可以发现,若序列中有 \(x\)\(-1\)\(y\)\(1\),那么要求 \(2x+y\equiv \pmod{3}\),同时要求序列中一定要有两个相邻的相同元素。

考虑证明这个条件是充要的,先证明必要性,我们假设这个结论对于 \(n\le k\) 都是成立的,考虑 \(n=k+1\),那么序列必定被拆为 \(A+B\) 两部分再合并,简单计算可以得出符合这个式子。

然后证明充分性,我们会直接给出一种构造。

我们使用递归进行构造,从最后的操作反向考虑。

首先讨论左右元素,如果有 \(-1\),那么我们直接把他扔掉,转化为 \(n-1\) 的构造。

如果左右没有 \(-1\),我们可以说明序列中一定可以找到两个不相交的连续相同的元素,如果只有一对,那么形如 \(1,-1,1\cdots 1,1,1\cdots 1, -1,1\) 或者 \(1,-1,1\cdots -1,-1,-1\cdots 1, -1,1\),中间是 \(2/3\)\(-1\) 或者 \(2/3\)\(1\)

计算之后发现并不和模 \(3\) 的性质。

那么我们找到第一对相同,如果是 \(-1,-1\) 那么直接在第二个 \(-1\) 后断掉,两侧仍符合性质。

如果是 \(1,1\),那么同样直接在这里断掉,两侧也是符合性质的。


有了这个性质我们就可以直接 dp,记 \(dp_{i,0/1/2,0/1,0/1}\) 代表前 \(i\) 个数,目前余数为 \(0/1/2\)\(i\) 填的是 \(-1/1\),没有/已经有一组相邻的了。

直接转移即可,代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5 + 5;
int n, a[maxn], dp[maxn][3][2][2];
signed main() {
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	if(n == 1) {
		cout << a[1] << endl;
		return 0;
	}
	memset(dp, -0x3f, sizeof(dp));
	dp[1][1][0][0] = a[1], dp[1][2][1][0] = -a[1];
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= 2; j++)
			for (int k = 0; k <= 1; k++) {
				for (int l = 0; l <= 1; l++) {
				//	cout << i << " " << j << " " << k << " " << l << " " << dp[i][j][k][l] << endl;
					for (int x = 0; x <= 1; x++) {
						int v = (x == 0 ? 1 : 2);
						dp[i + 1][(j + v) % 3][x][(x == k) | l] = max(dp[i + 1][(j + v) % 3][x][(x == k) | l], dp[i][j][k][l] + (x == 0 ? a[i + 1] : -a[i + 1]));
					}
				}
			}
	}
	cout << max(dp[n][1][0][1], dp[n][1][1][1]) << endl;
	return 0;
}
posted @ 2025-07-25 17:02  LUlululu1616  阅读(30)  评论(0)    收藏  举报