题意
给出一个长度为奇数\(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]); }
后记
我是来抄作业的。详见集训队作业题解。