【题解】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;
}