正题
题目链接:https://www.luogu.com.cn/problem/AT3950
题目大意
一个包含?,0,1?,0,1?,0,1的长度为奇数的序列,把???替换为0/10/10/1。每次可以选择三个数变成它们的中位数,求有多少种替换方案使得能够把序列最终变为一个111。
1≤∣S∣≤3×1051\leq |S|\leq 3\times 10^51≤∣S∣≤3×105
解题思路
好像是XJXJXJ那边杂题选讲时候的题
考虑优先减少000的数量。考虑一些一定优的情况000−>0,01−>∅000->0,01->\varnothing000−>0,01−>∅。
这样序列就会变为前面都是111,后面是1/21/21/2个000的情况。此时如果111的数量不少于000的数量就可以了。
那么111的数量超过222的部分也没有意义,设fi,jf_{i,j}fi,j表示现在到底iii个时抵消的前面有iii个111,后面jjj个000时的方案数。显然i,j∈[0,2]i,j\in[0,2]i,j∈[0,2],转移即可。
时间复杂度O(n)O(n)O(n)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=3e5+10,P=1e9+7;
ll f[N][3][3],n,ans;
char s[N];
signed main()
{scanf("%s",s);n=strlen(s);f[0][0][0]=1;for(ll i=0;i<n;i++){for(ll j=0;j<3;j++)//num0 for(ll k=0;k<3;k++){//num1 (0 on the 1)if(s[i]=='?'||s[i]=='0'){if(j==2)(f[i+1][1][k]+=f[i][j][k])%=P;else (f[i+1][j+1][k]+=f[i][j][k])%=P;}if(s[i]=='?'||s[i]=='1'){if(j)(f[i+1][j-1][k]+=f[i][j][k])%=P;else (f[i+1][j][min(k+1,2ll)]+=f[i][j][k])%=P;}}}for(ll j=0;j<3;j++)for(ll k=j;k<3;k++)(ans+=f[n][j][k])%=P;printf("%lld\n",ans);return 0;
}