[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\) 匹配,滿足以下條件:
- \(|S_x|=|T|\)。
- 對於任意的 \(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;
}