题解 AGC046D 【Secret Passage】
题意
给定
求最后能得到多少种不同的字符串。
题解
首先考虑每一次删除前两个的时候,保留的那个不要立刻插回去,而是当做一个自由的数。用一个三元组
所以现在我们最多有
对于一个字符串
然后想一想不难发现,对于最后得到的三元组相同的
朴素实现复杂度是四次方的,但是直接过了。
优化到三次方不难。但是我是懒狗。
代码
const int N=3e2+10;
int n;char s[N];int a[N],sum[N];
bool dp1[N][N][N];//dp1[i][j][k]表示剩下 s[i:] 有 j 个自由的 0 和 k 个自由的 1 是否可行
mint dp2[N][N][N];//dp2[i][j][k]表示从后往前撇匹配,能匹配上 s[i:]、j 个 0、k 个 1 的字符串数量
bool can[N][N][N];//can[i][j][k]表示dp2[i][j][k]能否被表示出来
signed main(){
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(a[i]=s[i]-'0');
dp1[1][0][0]=1;
for(int i=1;i<=n+1;i++)for(int j=n;~j;j--)for(int k=n-j;~k;k--)if(dp1[i][j][k]){
//考虑消一步有那些情况
if(i<=n-1){
if(a[i]==0||a[i+1]==0)dp1[i+2][j+1][k]=1;
if(a[i]==1||a[i+1]==1)dp1[i+2][j][k+1]=1;
}
if(i<=n){
if(a[i]==0&&(j||k))dp1[i+1][j][k]=1;
if(a[i]==0&&k)dp1[i+1][j+1][k-1]=1;
if(a[i]==1&&(j||k))dp1[i+1][j][k]=1;
if(a[i]==1&&j)dp1[i+1][j-1][k+1]=1;
}
if(j>=2)dp1[i][j-1][k]=1;
if(k>=2)dp1[i][j][k-1]=1;
if(j&&k)dp1[i][j-1][k]=1,dp1[i][j][k-1]=1;
}
dp2[n+1][0][0]=1;
for(int i=n+1;i;i--)for(int j=0;j<=n;j++)for(int k=0;j+k<=n;k++)if(dp2[i][j][k].x){
if(a[i-1]==0)dp2[i][j][k+1]+=dp2[i][j][k];
else dp2[i][j+1][k]+=dp2[i][j][k];
dp2[i-1][j][k]+=dp2[i][j][k];
}
for(int i=1;i<=n+1;i++)
for(int j=0;j<=n;j++)for(int k=0;j+k<=n;k++)if(dp1[i][j][k])
for(int w=i;w>=1;w--){
int cnt1=sum[i-1]-sum[w-1],cnt0=i-w-cnt1;
int jj=j-cnt0,kk=k-cnt1;
if(jj<0||kk<0)break;
can[w][jj][kk]=1;
}
mint ans=0;
for(int i=1;i<=n+1;i++)for(int j=0;j<=n;j++)for(int k=0;j+k<=n;k++)if(can[i][j][k])
ans+=dp2[i][j][k];
writeln(ans.x);
}