1. 程式人生 > 其它 >【題解】[SDOI2009]Bill的挑戰

【題解】[SDOI2009]Bill的挑戰

[SDOI2009]Bill的挑戰

\(\text{Solution:}\)

最初的 naive 想法是直接列舉子集算出每個子集中全部匹配的對應 \(T\) 串的個數,但是腦殘到最後寫完才反應過來會算重……

但好像用二項式反演還能搞回來(霧)

考慮 dp : 設 \(dp[i][j]\) 表示填充好 \([1,i]\) 的長度,能匹配的子集狀態是 \(j\) 的方案數。

那麼可以考慮用刷表法,列舉 \(i+1\) 位置填什麼。一共 \(26\) 種組合。所以可以預處理出 \(i\) 位置能匹配字元 \(j\) 的狀態集合,轉移就可以是:

\[\text{dp[i+1][j&state]+=dp[i][j]} \]

就是對能匹配的集合求交集。

於是這樣的方案數一定是不重複的,因為每一次填上位置的字元都不同。

不是說匹配到的字串不同,而是我們自己構造出的 \(T\) 是不同的。

重點在於讓你每次構造出來的 \(T\) 都是不同的,這樣才會保證不重不漏。

從這角度出發,進而啟發我們對字串 \(T\) 進行 \(dp\),狀態是和 \(T\) 息息相關的。

重點在於要反思設計狀態後注意一定不能把狀態給算重複了,一定要考慮最容易算重的是什麼。

這個時候往往可以考慮一下狀態對它設計,這樣更有利於我們對它去重計數的處理。

傻逼錯誤:陣列開小了,調了半天,下次寫程式碼就直接把陣列拉到最不可能出錯的大小就行了。

#include<bits/stdc++.h>
using namespace std;
const int mod=1000003;
inline int Mod(int x) {
	return (x>=mod?(x-mod):(x<0?(x+mod):x));
}
inline int Max(int x,int y) {
	return x>y?x:y;
}
inline int Min(int x,int y) {
	return x<y?x:y;
}
inline int Add(int x,int y) {
	return Mod(x+y);
}
inline int Mul(int x,int y) {
	return 1ll*Mod(x)*Mod(y)%mod;
}
int T,n,dp[51][1<<17],len,K;
int match[51][26];
char A[17][60];
inline int pop_count(int x) {
	int res=0;
	while(x!=0) {
		res+=x&1;
		x>>=1;
	}
	return res;
}
inline void Clear() {
	memset(dp,0,sizeof dp);
	memset(match,0,sizeof match);
}
int main() {
	freopen("111.txt","r",stdin);
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&n,&K);
		Clear();
		for(int i=1; i<=n; ++i) {
			scanf("%s",A[i]+1);
		}
		len=strlen(A[1]+1);
		int w=(1<<n)-1;
		dp[0][w]=1;
		for(int i=1; i<=len; ++i) {
			for(int j=0; j<26; j++) {
				for(int k=1; k<=n; k++) {
					if(A[k][i]=='?'||A[k][i]-'a'==j) {
						match[i][j]|=(1<<(k-1));
					}
				}
			}
		}
		for(int i=0; i<len; ++i) {
			for(int j=0; j<(1<<n); ++j) {
				for(int k=0; k<26; ++k) {
					int state=j&match[i+1][k];
					dp[i+1][state]+=dp[i][j];
					if(dp[i+1][state]>=mod)dp[i+1][state]-=mod;
				}
			}
		}
		int ans=0;
		for(int i=0; i<(1<<n); ++i) {
			if(__builtin_popcount(i)==K) {
				ans=ans+dp[len][i];
				if(ans>=mod)ans-=mod;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}