【好题分享】Median Replace

水题分享 AtCoder 提交地址

【题目描述】

给定一个奇数长度的 \(01\)\(S\),其中有若干个位置是 ? 字符。

每次可以将 \(3\) 个连续的字符替换成这三个数的中位数。求有多少种方案将 ? 替换成 \(0/1\) 使得进行 \(\dfrac{N-1}{2}\) 次操作后的字符串是 \(1\)

【样例输入#1】
1??00
【样例输出#1】
2
【样例输入#2】
?0101???10???00?1???????????????0????????????1????0
【样例输出#2】
402589311
【数据规模与约定】
  • \(1\leq |S| \leq 300000\)
  • 保证 \(|S|\) 为奇数;
  • \(S\) 只由 01? 构成。

首先,我们先不考虑 ? 的情况,对于一个字符串 \(S\) ,我们可以用栈来维护这个字符串是否合法。

对于 \(01\)\(10\) 这样的串,新的字符加入后,操作得到的字符肯定是新加的字符。

对于 \(000\) 这样的串,直接操作肯定是最优的。

所以我们考虑 \(000,01,10,111\) 这四个串;

很显然,我们要先删除 \(000\),然后是 \(01,10\),最后是 \(111\)

具体来说,这个栈从栈底到栈顶由一段连续的 \(1\) 和一段连续的 \(0\) 组成。

对于每个新加入栈的字符 \(c\),我们分情况考虑:

\(c\) 是字符 \(0\) 时,如果堆顶有两个 \(0\),我们将这三个 \(0\) 合并后得到一个 \(0\);当堆顶是 \(1\) 时,因为 \(000\) 的优先级在 \(01\) 之前,这个新加入的 \(0\) 可能会与后面的字符组成 \(000\) 的串,所以我们直接将它加入栈。

\(c\) 是字符 \(1\) 时,如果堆顶是 \(0\),我们直接将这两个字符抵消;否则,将 \(1\) 入栈。

对于一个 ?,我们在转移时,把他既按 \(1\),又按 \(0\) 转移就可以了。

然后因为要求最后得到的字符是 \(1\),所以最后栈中的 \(cnt_0\leq cnt_1\),因为 \(cnt_0 \leq 2\),所以,我们把 \(cnt_1>2\) 的情况存在 \(cnt_1=2\) 的情况里就可以了。

因此我们设 \(f_{i,j,k}\) 表示枚举到第 \(i\) 个字符,且当前的堆中有 \(j\)\(1\)\(k\)\(0\) 的情况。

然后直接转移就好了

最后记录答案的时候,答案也就为 \(f_{n,j,k}(j\geq k)\) 的和。

代码如下:

#include<bits/stdc++.h>
#define rint register int
using namespace std;
inline int read(){
    int s=0,f=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
    while(c>='0'&&c<='9') s=(s<<1)+(s<<3)+(c^48),c=getchar();
    return f?s:-s;
}
const int Mod=1e9+7;
char s[300010];
int n,f[300010][3][3],ans;
int main(){
    scanf("%s",s+1); n=strlen(s+1);
    f[0][0][0]=1;
    for(rint i=0;i<n;++i)
        for(rint j=0;j<=2;++j)
            for(rint k=0;k<=2;++k){
                if(s[i+1]!='0'){
                    if(k) f[i+1][j][k-1]=1ll*(f[i+1][j][k-1]+f[i][j][k])%Mod;
                    else f[i+1][min(j+1,2)][k]=1ll*(f[i+1][min(j+1,2)][k]+f[i][j][k])%Mod;
                }
                if(s[i+1]!='1'){
                    if(k==2) f[i+1][j][1]=1ll*(f[i+1][j][1]+f[i][j][k])%Mod;
                    else f[i+1][j][k+1]=1ll*(f[i+1][j][k+1]+f[i][j][k])%Mod;
                }
            }
    for(rint i=0;i<=2;++i)
        for(rint j=0;j<=i;++j) ans=1ll*(ans+f[n][i][j])%Mod;
    printf("%d",ans);
    return 0;
}
posted @ 2020-08-21 14:21  LCGUO  阅读(150)  评论(0编辑  收藏  举报