Loading

【题解】CF1421E Swedish Heroes

找规律题。

不难想出区间 \(\rm DP\) ,但是 \(\mathcal{O}(N^3)\) 已经是信息极限了。

观察这道题的特殊性质。

我们只关注最终序列,每一个位置是否取反,所以我们打一个暴搜的代码。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 55
using namespace std;
int n,a[N],v[N];
void solve(int k){
	if(k == n){
		rep(i,1,n){
			if(a[i])putchar('-');
			else putchar('+');
		}
		putchar('\n');
		return;
	}
	int b[N];
	rep(i,1,n)b[i] = a[i];
	rep(i,1,n-1)if(!v[i]){
		v[i] = 1;
		int j = i;
		a[i] ^= 1;a[i + 1] ^= 1;
		while(j > 1 && v[j - 1])a[--j] ^= 1;
		j = i + 1;
		while(j < n && v[j])a[++j] ^= 1;
		solve(k + 1);
		v[i] = 0;
		rep(i,1,n)a[i] = b[i];
	}
}
int main(){
	scanf("%d",&n);
	solve(1);
	return 0;
} 

输入 \(n\) ,得到所有可能的序列,然后我们发现这些序列的加号个数有一定的规律。

下面是表,\(S\) 表示加号个数的集合。

n   S
1   1
2   0
3   2
4   1,4
5   0,3
6   2,5
7   1,4,7

不难发现 \((n+sum_{+})\bmod 3=2\)。同时序列中必定有两个相邻的位置符号相同。

不得不说这题的样例非常良心,指出了必须存在邻项符号相同的情况。

所以我们设计状态 \(f[i][0/1/2][0/1][0/1]\) 表示前 \(i\) 个数,当前和 \(\bmod 3\) 后的值,上一个位置是 \(+/-\),是否已经有邻项相同的位置。

时间复杂度 \(\mathcal{O}(N)\),转移时有 \(24\) 倍常数。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
using namespace std;
int n,a[N];long long f[N][3][2][2];
inline void ck(long long &x,long long y){if(y > x)x = y;}
int main(){
	scanf("%d",&n);
	rep(i,1,n)scanf("%d",&a[i]);
	if(n==1){printf("%d\n",a[1]);return 0;}
	memset(f,0xcf,sizeof(f));
	f[1][n % 3][0][0] = -a[1];
	f[1][(n + 1) % 3][1][0] = a[1];
	rep(i, 2, n)rep(j, 0, 2)rep(k, 0, 1)rep(p, 0, 1)rep(w, 0, 1)
		ck(f[i][(j + k) % 3][k][w | (k == p)], f[i - 1][j][p][w] + (k * 2 - 1) * a[i]);
	printf("%lld\n",max(f[n][2][0][1], f[n][2][1][1]));
	return 0;
}
posted @ 2021-10-04 16:57  7KByte  阅读(39)  评论(0编辑  收藏  举报