题意
给出一个长度为奇数(n)的残缺01
串,问有多少种补全方法,每次将连续三个位替换为它们的中位数后,能有一种方案使它变为1
。
(n le 3*10^5)
思路
左边表示栈顶。
将操作简化为:将000
变为0
;将111
变为1
;删掉相邻的01
或10
. 考虑这些操作的优先级,显然是每次有000
就执行,没有就执行01
或10
,都没有再执行 111
。同类内部的顺序并不影响结果。
现在考虑用栈维护,从左往右加入串中字符,如果加入了 1
,那么栈顶是 0
便可弹掉; 如果加入了 0
,由于 000
优先于其他,栈顶是 1
也暂时不操作,最后再考虑 10
和 111
操作。这样栈的形态尝试一下,就可以得出如下列举的,只有这么几种。
注意当(1)的个数(ge 2)时,肯定是可行的,可以等价到两个的情况。
因为最后只要有两个(1)或者1
。加起来,输出就可以了
我的状态是这样的:-
, 0
,1
,00
,01
,11
,001
,011
,0011
;
#include <bits/stdc++.h>
const int N=300005,mu=1000000007;
const int tran[2][N]={{1,3,4,1,6,7,4,8,7},{2,0,5,1,2,5,4,5,7}};
int l,dp[N][10];
char s[N];
void reduce(int &x){x+=x>>31μ}
int main(){
scanf("%s",s+1);
int l=strlen(s+1);
dp[0][0]=1;
for (int i=1;i<=l;i++){
for (int j=0;j<=8;j++){
if (s[i]!='1') reduce(dp[i][tran[0][j]]+=dp[i-1][j]-mu);
if (s[i]!='0') reduce(dp[i][tran[1][j]]+=dp[i-1][j]-mu);
}
}
reduce(dp[l][5]+=dp[l][7]-mu);
reduce(dp[l][5]+=dp[l][8]-mu);
reduce(dp[l][5]+=dp[l][2]-mu);
printf("%d",dp[l][5]);
}
后记
我是来抄作业的。详见集训队作业题解。