[SDOI2009] Bill的挑戰

辜铜星發表於2024-05-29

[SDOI2009] Bill的挑戰

題目資訊

題目描述

Sheng_bill 不僅有驚人的心算能力,還可以輕鬆地完成各種統計。在昨天的比賽中,你憑藉優秀的程式與他打成了平局,這導致 Sheng_bill 極度的不滿。於是他再次挑戰你。這次你可不能輸。

這次,比賽規則是這樣的:

給出 \(N\) 個長度相同的字串(由小寫英文字母和 ? 組成),\(S_1,S_2,\dots,S_N\),求與這 \(N\) 個串中的剛好 \(K\) 個串匹配的字串 \(T\) 的個數,答案對 \(1000003\) 取模。

若字串 \(S_x(1\le x\le N)\)\(T\) 匹配,滿足以下條件:

  1. \(|S_x|=|T|\)
  2. 對於任意的 \(1\le i\le|S_x|\),滿足 \(S_x[i]= \texttt{?}\) 或者 \(S_x[i]=T[i]\)

其中 \(T\) 只包含小寫英文字母。

輸入格式

本題包含多組資料

第一行一個整數 \(T\),表示資料組數。

對於每組資料,第一行兩個整數,\(N\)\(K\)

接下來 \(N\) 行,每行一個字串 \(S_i\)

輸出格式

每組資料輸出一行一個整數,表示答案。

樣例 #1

樣例輸入 #1

5

3 3

???r???

???????

???????

3 4

???????

?????a?

???????

3 3

???????

?a??j??

????aa?

3 2

a??????

???????

???????

3 2

???????

???a???

????a??

樣例輸出 #1

914852

0

0

871234

67018

資料規模與約定

  • 對於 \(30\%\) 的資料,\(N\le5\)\(|S_i|\le20\)
  • 對於 \(70\%\) 的資料,\(N\le13\)\(|S_i|\le30\)
  • 對於 \(100\%\) 的資料,\(1\le T\le 5\)\(1\le N \le15\)\(1\le|S_i|\le50\)

思路分析

這道題和[CEOI2010 day2] pin真的差不多。

顯然

\[g(S)=\sum_{T\supseteq S}{f(T)} \]

根據廣義容斥原理

\[f(S)=\sum_{T\supseteq S}{(-1)^{|S|-|T|}g(T)} \]

\(g(S)\) 直接暴力求就行了,卡個常,就過了!

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 5e4+10;
namespace math{
	int MOD;
	int frac[MAXN];
	int qpow(int a,int b){
		if(b==0) return 1;
		if(b==1) return a;
		int k = qpow(a,b>>1);
		k*=k;k%=MOD;
		if(b&1) k*=a;k%=MOD;
		return k;
	}
	int inv(int a){
		return qpow(a,MOD-2);
	}
	int C(int n,int m){
		if(n<m) return 0;
		return frac[n]*inv(frac[m]*frac[n-m]%MOD)%MOD;
	}
	int get(int n,int m){
		return C(m+n-1,m);
	}
	void init(){
		frac[0] = 0;
		for(int i = 1;i<MAXN;i++){
			frac[i] = frac[i-1]*i;
			frac[i]%=MOD;
		}
	}
	int lowbit(int k){
		return k&(-k);
	}
	int bit(int k){
		int sum = 0;
		while(k){
			sum ++;
			k -= lowbit(k);
		}
		return sum;
	}
	int log2(int k){
		for(int i = 0;i<=64;i++){
			int p = 1;
			if(p<<i==k) return i;
		}
		return 0;
	}
}
using namespace math;
int n,m,k,g[1<<16],f[1<<16];
bool can[1<<16];
vector<int> v[1<<16];
char tmp[20][60];
void read(int &n){
	char tmp;
	int x = 1;
	do tmp = getchar();
	while(!(tmp=='-'||('0'<=tmp&&tmp<='9')));
	if(tmp=='-'){
		x = -1;
	}
	int gz = 0;
	while('0'<=tmp&&tmp<='9'){
		gz*=10;
		gz+=tmp-'0';
		tmp = getchar();
	}
	n = gz*x;
}
namespace code{
	using namespace math;
signed main(){
	read(n);read(k);
	for(int i = 0;i<n;i++){
		char k;
		k = getchar();
		int u = 0;
		while(k!='\n'){
			tmp[i][u++] = k;
			k = getchar();
		}
		tmp[i][u] = '\0';
	}
	m = strlen(tmp[1]);
	vector<int> p;
	for(int i = 0;i<(1<<n);i++){
		can[i] = 1;g[i] = 1;
		for(int j = 0;j<m;j++){
			char k = '?';
			for(int u:v[i]){
				if(tmp[u][j]!='?'){
					if(k=='?') k = tmp[u][j];
					else if(k!=tmp[u][j]){
						can[i] = false;
						break;
					}
				}
			}
			if(!can[i]) break;
			if(k=='?') g[i]*=26;
			g[i]%=MOD;
		}
		if(!can[i]) g[i] = 0;
		p.push_back(i);
	}
	int ans = 0;
	for(int S:p){
		f[S] = 0;
		if(bit(S)==k){
			for(int T:p){
				if((T&S)==S){
					f[S] += qpow(-1,bit(T)-bit(S))*g[T]%MOD;
					f[S] %= MOD;
				}
			}
			ans += f[S];
			ans%=MOD;
		}
	}
//	cout << 0b1010 << endl;
	cout << (ans%MOD+MOD)%MOD << '\n';
	return 0;
}
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	math::MOD = 1e6+3;
	frac[0] = 1;
	for(int i = 1;i<=MAXN-1;i++){
		frac[i] = frac[i-1]*i;
	}
	for(int i = 0;i<(1<<15);i++){
		int k = i;
		while(k){
			v[i].push_back(math::log2(lowbit(k)));
			k-=lowbit(k);
		}
	}
	int T;
	read(T);
	while(T--){
		code::main();
	}
	return 0;
}

相關文章