一看题解好像全是状压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; }
原文地址:https://www.cnblogs.com/ivanovcraft/p/9593760.html
时间: 2024-11-08 13:10:27