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

【[SDOI2009]Bill的挑戰】

數組 ostream () nbsp std san d+ 一個 原理

一看題解好像全是狀壓DP,那麽我就來補充一個容斥寫法吧

乍一看,好像是水題,枚舉選哪k個串,然後判斷

1,如果這k個串中至少兩個串某位置確定且不相同,答案顯然為0
2,如果這個位置只被有且僅有一個串確定,這個位置就唯一確定了
3,否則這個位置有26種不同填數情況,統計答案時只要用乘法原理搞一下就行

但是容易想到,這樣做是有問題的,以樣例的第一組數據為例
我們選定串1,2,然後發現第四個位置確定是r,其他位置任選,但是無論我們構造出怎樣的串,T總是可以同時匹配串3的

考慮容斥掉這些匹配到更多串的方案

首先,我們可以用上述方法求出匹配至少i個串的方案數,記為num[i]
我們需要統計恰好滿足匹配i個的情況,記為ans[i]


現在問題來了,怎麽容斥

考慮ans[i]與ans[j]的聯系(i>j),定義保證ans[j]是恰好匹配j個串
如果再匹配到i-j個串,就是ans[j]
在i個串中,這i-j個串的選擇當然就有C(i,i-j)種方案
我們有num[j],得出公式ans[j]=num[j]-∑C(i,i-j)*ans[i]
倒序處理ans數組即可

上代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod=1000003;
int t,n,k,num[20],len,c[20][20],ans[20]; char s[20][60]; int ksm(int x,int t) { int ret=1; while(t) { if(t&1) ret=(ll)ret*x%mod; x=(ll)x*x%mod,t>>=1; } return ret%mod; } int check(int x) { char tmp[60]; for(int i=0;i<len;i++) tmp[i]
=?; int rest=len; for(int i=0;i<n;i++) { if(x&(1<<i)) for(int j=0;j<len;j++) if(!isalpha(tmp[j])&&isalpha(s[i+1][j])) { tmp[j]=s[i+1][j]; rest--; } else if(isalpha(tmp[j])&&isalpha(s[i+1][j])&&tmp[j]!=s[i+1][j]) return 0; } return ksm(26,rest); } int main() { for(int i=0;i<20;i++){ c[i][0]=1; for(int j=1;j<=i;j++){ c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod; } } scanf("%d",&t); while(t--) { memset(num,0,sizeof(num)); scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%s",s[i]); len=strlen(s[1]); int mul=1; if(k>n) { printf("0\n"); continue; } for(int i=1;i<(1<<n);i++) { int cnt=0; for(int j=0;j<n;j++) if(i&(1<<j)) cnt++; if(cnt<k) continue; (num[cnt]+=check(i))%=mod; } for(int i=n;i>=k;i--) { int sum=0; for(int j=i+1;j<=n;j++) (sum+=(ll)c[j][i]*ans[j]%mod)%=mod; ans[i]=((num[i]-sum)%mod+mod)%mod; } printf("%d\n",(ans[k]%mod+mod)%mod); } return 0; }

【[SDOI2009]Bill的挑戰】