CF796E题解
注意到本质不同的偷看只有 \(2n\) 种,于是先设 \(dp[n][m]\) 表示前 \(n\) 个位置进行 \(m\) 次操作的最大值。
我们还需要知道上一次在两边进行操作的位置,不难发现这个位置一定在 \(k\) 步以内。于是设 \(dp[n][m][x][y]\) 表示两个序列上次偷看的位置是 \(n-x,n-y\),如果 \(x,y>k\) 那么显然令其等于 \(k\) 是没问题的。
这样就可以 \(O(n^2k^2)\)了。
但是这样子难优化,重新设状态 \(dp[n][m][x][y]\) 表示两个序列分别还可以偷看 \(x\) 和 \(y\) 个位置。
显然可以 \(dp[n][m][x][y]\to dp[n][m+1][k-1][y]/dp[n][m+1][x][k-1]/dp[n][m+2][x][y]\)。
而且很显然的是,进行这个操作的话显然是 \(dp[n][m][0][y]\) 或者 \(dp[n][m][x][0]\) 最优。
因为如果区间重合了,那么对区间稍微平移一下的可能偷看到答案的题的集合一定会比当前集合大。
所以问题相当于变成取一整行或者一整列的最值。
接下来考虑 \(dp[n][m][x][y]\to dp[n+1][m][x-1][y-1]\)。考虑用一个数来表示现在的 \((r,r)\) 是真正的 \((0,0)\)。这样子就只需要进行行加和列加即可。
容易发现每次不会被修改的位置一定是一行/一列/\((x,x)\)。
因为只有行修改和列修改以及单点修改,直接对每行和每列永久化加法懒标记即可。
时间复杂度 \(O(npk)\)。
然后因为显然,若 \(p\geq2\lfloor\frac{n+k-1}{k}\rfloor\) 那么直接统计所有能做的题即可,可以卡上界,此时 \(p\) 是 \(O(\frac{n}{k})\) 级别的,复杂度变为 \(O(n^2)\)。
#include<cstdio>
#include<cctype>
namespace SOLVE{
const int M=1005;
int n,m,k,tx[55],ty[55],id[M],w[M][55][55];bool v1[M],v2[M];
inline int max(const int&a,const int&b){
return a>b?a:b;
}
inline int read(){
int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
}
inline void main(){
int ans(0);n=read();m=read();k=read();
for(int m=read();m--;)v1[read()]=true;for(int m=read();m--;)v2[read()]=true;
if(m>2*(int(n+k-1)/k)){
int ans(0);for(int i=1;i<=n;++i)(v1[i]||v2[i])&&++ans;printf("%d",ans);return;
}
for(int j=0;j<=m;++j)for(int x=0;x<=k;++x)for(int y=0;y<=k;++y)w[j][x][y]=-114514;
for(int j=0;j<=m;++j)w[j][0][0]=0;
for(int i=0;i<=n+1;++i)id[i]=(i+k)%(k+1);
for(int i=1;i<=n;++i){
const int&a=id[i-1],&b=id[i],&c=id[i+1];
for(int j=m;j>=0;--j){
int mx(0);
for(int x=0;x<=k;++x)mx=max(mx,max(tx[b]+ty[x]+w[j][b][x],tx[x]+ty[b]+w[j][x][b]));
if(j+2<=m&&mx>w[j+2][a][a]+tx[a]+ty[a])w[j+2][a][a]=mx-tx[a]-ty[a];
for(int x=0;x<=k;++x){
if(tx[b]+ty[x]+w[j][b][x]>tx[a]+ty[x]+w[j+1][a][x]){
w[j+1][a][x]=tx[b]+ty[x]+w[j][b][x]-tx[a]-ty[x];
}
if(tx[x]+ty[b]+w[j][x][b]>tx[x]+ty[a]+w[j+1][x][a]){
w[j+1][x][a]=tx[x]+ty[b]+w[j][x][b]-tx[x]-ty[a];
}
}
}
if(v1[i]&&v2[i]){
for(int x=0;x<=k;++x)++tx[x];for(int j=0;j<=m;++j)--w[j][b][b];
}
else if(v1[i]){
for(int x=0;x<=k;++x)if(x!=b)++tx[x];
}
else if(v2[i]){
for(int x=0;x<=k;++x)if(x!=b)++ty[x];
}
for(int j=0;j<=m;++j){
for(int x=0;x<=k;++x)if(x!=b){
if(tx[b]+w[j][b][x]>tx[c]+w[j][c][x])w[j][c][x]=tx[b]+w[j][b][x]-tx[c];
if(ty[b]+w[j][x][b]>ty[c]+w[j][x][c])w[j][x][c]=ty[b]+w[j][x][b]-ty[c];
}
if(tx[b]+ty[b]+w[j][b][b]>tx[c]+ty[c]+w[j][c][c])w[j][c][c]=tx[b]+ty[b]+w[j][b][b]-tx[c]-ty[c];
for(int x=0;x<=k;++x)w[j][b][x]=w[j][x][b]=-114514;
ans=max(ans,w[j][c][c]+tx[c]+ty[c]);
}
}
printf("%d",ans);
}
}
signed main(){
SOLVE::main();
}
调了好久,阴间死我了!