【[SDOI2009]Bill的挑戰】
阿新 • • 發佈:2018-09-05
數組 ostream () nbsp std san d+ 一個 原理
現在問題來了,怎麽容斥
考慮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數組即可
上代碼:
一看題解好像全是狀壓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的挑戰】