6730. 【2020.06.17省选模拟】T1 普及良

题目


正解

毒瘤DYP随便AC的题。

先考虑没有问号的时候怎么做。
手玩一下,模拟一下操作的过程,可以发现长这样:
维护一个栈,每次加入一个数进去,并且选择是否和整个栈中的数合并。
为了方便操作,考虑两个两个加进去(不然状态数会变得很多)。
对于一个栈,可以发现实际上它对后面的数的贡献相当于一个函数:G(x,0/1)G(x,0/1),表示如果新加进来一个数0011,会生成什么样的数。
加入两个数,要么两个都是直接加入栈中,要么先加一个和栈内元素合并。两个操作都可以比较方便地从旧函数运算得到新函数。
于是设DP:fi,0/1,0/1f_{i,0/1,0/1}表示加入了前ii个数,后面加入0011之后就变成0011,是否可能得到。

有问号怎么做呢?
如果硬是套上面的做法,发现这会算重。因为一种方案可能会通过多种操作得到结果。
或者也可以用另一种方式来解释这种现象:在上面的转移中,就算没有遇到问号,一次转移也会分裂成两个状态,这两个状态继承了同样的方案数,这就自然会算重。
接下来就如DYP所言,将若干个状态“绑”在一起。
具体是什么呢,用个44位的二进制状态,分别表示对于后面两维取00,10,01,1100,10,01,11时,fi,0/1,0/1f_{i,0/1,0/1}等于什么。
gi,Sg_{i,S}表示处理完前ii个数,状态为SS的方案数。可以发现如果不遇到问号,转移到的新状态是唯一的。
具体处理的时候,可以对于每个状态先预处理一下后面加入两个数之后,会变成哪个状态。用位运算乱搞即可。

gmh77提供了一种绝妙的理解方法:
可以认为当前的状态是不确定的,但是它们共用一个方案数。这就好比薛定谔的猫,真实的状态处于量子态,如果不去进行观测就不知道是什么,只能将若干个小状态的集合看做一个整体转移。

这就是传说中的——DP套DP……


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define mo 998244353
#define ll long long
int n;
int p[N];
char str[N];
int s[N];
int to[16][2][2];
int f[N][16];
void add(int &a,int b){a=(a+b)%mo;}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("junior.in","r",stdin);
	freopen("junior.out","w",stdout);
	int T;
	scanf("%d",&T);
	while (T--){
		scanf("%s",str);
		for (int i=0;i<8;++i)
			p[i]=str[i]-'0';
		for (int i=0;i<16;++i)
			for (int s1=0;s1<2;++s1)
				for (int s2=0;s2<2;++s2){
					int j=0;
					for (int k=0;k<2;++k){
						j|=(i>>s1+k*2&1)<<0+p[k+s2*2]*2;
						j|=(i>>s1+k*2&1)<<1+p[k+s2*2+4]*2;
						j|=(i>>p[s1+s2*2]+k*2&1)<<0+k*2;
						j|=(i>>p[s1+s2*2+4]+k*2&1)<<1+k*2;
		//				f'[0][p[k+s2*2]]|=f[s1][k];
		//				f'[1][p[k+s2*2+4]]|=f[s1][k];
		//				f'[0][k]|=f[p[s1+s2*2]][k];
		//				f'[1][k]|=f[p[s1+s2*2+4]][k];
					}
					to[i][s1][s2]=j;
				}
		scanf("%s",str+1);
		n=strlen(str+1);
		for (int i=1;i<=n;++i)
			s[i]=(str[i]=='?'?-1:str[i]-'0');
		memset(f,0,sizeof f);
		f[0][1+8]=1;
		for (int i=0;i+2<=n;i+=2){
			for (int j=0;j<16;++j){
				if (s[i+1]!=1 && s[i+2]!=1) add(f[i+2][to[j][0][0]],f[i][j]);
				if (s[i+1]!=0 && s[i+2]!=1) add(f[i+2][to[j][1][0]],f[i][j]);
				if (s[i+1]!=1 && s[i+2]!=0) add(f[i+2][to[j][0][1]],f[i][j]);
				if (s[i+1]!=0 && s[i+2]!=0) add(f[i+2][to[j][1][1]],f[i][j]);
			}
		}
		ll ans=0;
		for (int j=0;j<16;++j){
			if (s[n]!=1 && (j>>(0+1*2)&1)) 
				ans+=f[n-1][j];
			if (s[n]!=0 && (j>>(1+1*2)&1)) 
				ans+=f[n-1][j];
		}
		printf("%lld\n",ans%mo);
//		f[0][0][0]=f[0][1][1]=1;
//		for (int i=0;i+2<=n;i+=2)
//			for (int k=0;k<2;++k){
//				f[i+2][0][p[k+s[i+2]*2]]|=f[i][s[i+1]][k];
//				f[i+2][1][p[k+s[i+2]*2+4]]|=f[i][s[i+1]][k];
//				f[i+2][0][k]|=f[i][p[s[i+1]+s[i+2]*2]][k];
//				f[i+2][1][k]|=f[i][p[s[i+1]+s[i+2]*2+4]][k];
//			}
//		printf("%d\n",f[n-1][s[n]][1]);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章