[AGC022E]Median Replace

题目

传送门 to AtCoder

思路

首先考虑没有 ? 的时候怎么做。

注意到串中只剩 1 的时候,总是可以完成任务;也就是说,1 太多没关系,但有 0 就不行。所以我们会想到先去删除零。

接下来观察操作的性质:当 011001 操作时,都是 01 各减少一个;换句话说,相邻的 01 可以直接相互湮灭。但 000 操作可以使 0 直接减少 2 2 2 个,所以我们优先进行 000 型操作。

那么,进行第一轮简单化简后,就得到 1 和长度为 1 1 1 2 2 20 交错出现的串。接下来是否还有可能进行 000 操作呢?有。00100 型,可以通过 1 的湮灭使得 000 再次出现。之后就判断 01 数量多少就行。

但是这样就无法推广到 ? 存在的情况了——我们希望的是 线性扫描,即从左往右扫描即可判断答案,这样就可以逐个确定 ? 然后计数了。考虑能否线性扫描?

要找到方案的共性:无论是不是 00100 型,1 总是要立即与其左侧的 0 相互湮灭。因为 1 长的时候,与 0 湮灭等价于比较数量;1 短的时候,通过湮灭使得 0 相连。

于是可以维护一个形如 111...100 的串,末尾 0 最多 2 2 2 个。遇到 1 就与左侧 0 相互湮灭。遇到 0 就往后叠加,达到 3 3 30 就变为 1 1 1 个。线性扫描可行。

然而状态数是 O ( 3 n ) \mathcal O(3n) O(3n),完全不行。我们还需要注意到一个惊人的事实:最左侧的 1 数量只会增加。因为要么湮灭一个 0,要么增加一个 1,从来不会消耗 1 。而最后我们只需要判断 c n t 1 > c n t 0 cnt_1>cnt_0 cnt1>cnt0,所以只需要保留 3 3 31 即可。当然也可以更细致一点,由于 2 ∤ n 2\nmid n 2n,所以 c n t 1 ≠ c n t 0 cnt_1\ne cnt_0 cnt1=cnt0,于是也可以改为判断 c n t 1 ⩾ c n t 0 cnt_1\geqslant cnt_0 cnt1cnt0,只保留 2 2 21 就够了。

时间复杂度 O ( 3 2 n ) \mathcal O(3^2n) O(32n)

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
    
    
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MOD = 1e9+7;
inline void modAddUp(int &x,const int y){
    
    
	if((x += y) >= MOD) x -= MOD;
}

char str[300005];
int dp[2][3][3];
int main(){
    
    
	scanf("%s",str+1);
	int n = int(strlen(str+1));
	dp[0][0][0] = 1;
	for(int i=1,fr=0; i<=n; ++i,fr^=1){
    
    
		memset(dp[i&1],0,3*3<<2);
		rep(one,0,2) rep(zero,0,2){
    
    
			const int &w = dp[fr][one][zero];
			if(str[i] != '1') modAddUp(dp[i&1][one][(zero&1)+1],w);
			if(str[i] != '0'){
    
     // choose 1
				if(zero) modAddUp(dp[i&1][one][zero-1],w);
				else modAddUp(dp[i&1][one+1-(one>>1)][zero],w);
			}
		}
	}
	int ans = 0;
	rep(one,0,2) rep(zero,0,one)
		modAddUp(ans,dp[n&1][one][zero]);
	printf("%d\n",ans);
	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/qq_42101694/article/details/122405507
Recomendado
Clasificación