Loading

[题解] [AGC022E] Median Replace

题目大意

有个奇数长度的 \(01\)\(s\) 其中有若干位置是 \(?\)

每次可将 \(3\) 个连续的字符替换成这三个数的中位数。

求有多少方案将 \(?\) 替换成 \(0/1\) 使得进行 \(\frac{N-1}{2}\) 次操作后的字符串是 \(1\)

\(1 ≤ ∣S∣ ≤ 300000\)

解题思路

吐了,还想什么自己写,想了 114514 年也不懂哦

首先,对于这种求合法方案的,先看给出一个方案,怎么判断是否合法。

然后,对于这种选某一段消除/合并的,可以拿一个栈存着之前剩下的。

接下来考虑如何维护这个栈:

  • 加入一个 \(0\)

    • 栈顶有 \(2\)\(0\),一个显然的贪心是把三个 \(0\) 合并成一个;
    • 否则不知道怎么办,那就把 \(0\) 放进去;
  • 加入一个 \(1\)

    • 栈顶是 \(1\),另一个显然的贪心是不妨让 \(1\) 屯起来。

    • 栈顶是 \(0\),那么有两种可能: \(0\) 或者 \(00\) (由之前的贪心,不存在三个连续的 \(0\)),这些连续的 \(0\) 前面,可能有 \(1\),也可能没有。

      咕咕咕。

不想分析了,感觉分析不清了,直接放代码 qwq。

#include <set>
#include <map>
#include <queue>
#include <bitset>
#include <vector>
#include <math.h>
#include <ctype.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;

const int N(300005), mod(1e9 + 7);

int n, a[N];
int f[N][3][3];
char s[N];

inline void read(int &x){
	x = 0; int f = 1, c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)) x = x * 10 + c - 48, c = getchar();
	x *= f;
}

inline void MOD(int &x){ x = x + ((x >> 31) & mod); }
inline void upd(int x, int I, int O, int res){ MOD(f[x][I][O] += res - mod); }

int main(){
	scanf("%s", s + 1), n = strlen(s + 1);
	for(int i(1); i <= n; ++i) a[i] = s[i] == '?' ? -1 : s[i] - '0';
	if(n == 1) return cout << (a[1] != 0) << endl, 0;
	f[0][0][0] = 1;
	for(int i(0); i < n; ++i)
		for(int I(0); I <= 2; ++I)
		for(int O(0); O <= 2; ++O){
			int res = f[i][I][O];
			if(a[i + 1] != 1){// a[i + 1] == 0
				if(O == 2) upd(i + 1, I, 1, res);
				else upd(i + 1, I, O + 1, res);
			}
			if(a[i + 1] != 0){// a[i + 1] == 1
				if(O) upd(i + 1, I, O - 1, res);
				else if(I == 2) upd(i + 1, I, O, res);
				else upd(i + 1, I + 1, O, res);
			}
		}
	int ans = 0;
	for(int I(0); I <= 2; ++I)
	for(int O(0); O <= 2; ++O) 
		if(I >= O) MOD(ans += f[n][I][O] - mod);
	printf("%d\n", ans);
	return 0;
}
/* Hemerocallis */
posted @ 2021-09-23 20:31  IrisT  阅读(14)  评论(0编辑  收藏  举报