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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章