[AGC022E] Median Replace 解题报告
给出一个长度为奇数
n 的残缺01 串,问有多少种补全方法,每次将连续三个位替换为它们的中位数后,能有一种方案使它变为1 。
n\leq 3\times 10^5
解题思路:
考虑如何判断合法,题目操作本质是
维护一个栈,从左往右加入每一个字符,如果栈顶为
若新加入了 1,删去栈顶的 0 和新加入的 1。如果栈顶为 1 ,那么不管加入什么都留下。通过这个可以得出新的
栈里必定是若干
定义
-
若加入的是
1 - 若栈里已经有至少
1 个0 ,直接抵消,f_{i-1,j,k} \to f_{i,j,k-1} (01 无用) - 否则,若栈里有
2 个1 ,加入后无影响不改变f_{i-1,2,k} \to f_{i,2,k} - 否则,栈里
1 个数+1 f_{i-1,j,k} \to f_{i,j+1,k}
- 若栈里已经有至少
-
若加入的是
0 - 若栈里已有
2 个0 消成1 个,f_{i-1,j,2} \to f_{i,j,1} - 否则栈里
0 个数+1 f_{i-1,j,k} \to f_{i,j,k+1}
- 若栈里已有
-
若加入的是
? 分别当成
0/1 转移即可。
不难发现我们的
时间复杂度
代码:
char s[N];
int f[N][3][3],n,ans;
signed main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
#endif
f[0][0][0] = 1;
scanf("%s",s+1);
n = strlen(s+1);
for (int i = 1;i <= n;++i) {
for (int j = 0;j <= 2;++j) {
for (int k = 0;k <= 2;++k) {
if (s[i] == '0') {
if (k < 2) f[i][j][k+1] = (f[i][j][k+1] + f[i-1][j][k]) % mod;
else f[i][j][1] = (f[i][j][1] + f[i-1][j][k]) % mod;
}
if (s[i] == '1') {
if (k) {
f[i][j][k-1] = (f[i][j][k-1] + f[i-1][j][k]) % mod;
} else {
if (j < 2) f[i][j+1][k] = (f[i][j+1][k] + f[i-1][j][k]) % mod;
else f[i][2][k] = (f[i][2][k] + f[i-1][j][k]) % mod;
}
}
if (s[i] == '?') {
if (k < 2) f[i][j][k+1] = (f[i][j][k+1] + f[i-1][j][k]) % mod;
else f[i][j][1] = (f[i][j][1] + f[i-1][j][k]) % mod;
if (k) {
f[i][j][k-1] = (f[i][j][k-1] + f[i-1][j][k]) % mod;
} else {
if (j < 2) f[i][j+1][k] = (f[i][j+1][k] + f[i-1][j][k]) % mod;
else f[i][2][k] = (f[i][2][k] + f[i-1][j][k]) % mod;
}
}
}
}
}
for (int i = 0;i <= 2;++i) {
for (int j = 0;j <= i;++j) {
ans = (ans + f[n][i][j]) % mod;
}
}
printf("%lld\n",ans);
#ifndef ONLINE_JUDGE
fclose(stdin);fclose(stdout);
#endif
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。