smoj2075:匹配数(状压dp/广义容斥原理)
来源:互联网 发布:淘宝五折换购 编辑:程序博客网 时间:2024/06/08 08:53
明天就月考了,但我还在沉迷《白色相簿2》,虽然还没看到虐的地方,但前半段有很大的颠覆性,让我有点怀疑我的人生观。
题面:一个匹配模式是由一些小写字母和问号’?’组成的一个字符串。当一个由小写字母组成的字符串s,长度和匹配模式长度相同,并且在对应的每一位都相等或模式串相应位置是‘?’,则称字符串s与这个模式相匹配。
现给你 M 个匹配模式,它们长度相同,问恰好与其中有 K 个模式相匹配的字符串有多少个?(答案模1,000,003)
1<= M <= 15,模式长度len满足:1 <= len<= 50。
看到M很小可以考虑状压dp。对于二进制集合S,f[i][S]表示,对于前i个字母,仅满足的集合为S的方案数。对于第i+1位,枚举字母j,第i+1为满足j的匹配集合为P,则f[i][S]可以转移到f[i][S&P]。
若预处理P,复杂度为O(2^15*50*26)。
然而对于我这种萌新,但是直接枚举满足的集合S,算出满足S的方案数,若S的大小等于K则算进答案。然后爆10。
后来才知道原来我算重了很多东西,还有个叫广义容斥原理的东西。
广义容斥原理是用来把“至少”型问题转化为“恰好”型问题的机巧的计数方法。
有一个含有n个条件的集合,全集为P。f[i]为恰好满足i个条件的方案数,w[S]为满足集合S中条件的方案数,g[S]为S的元素个数。公式为
即对于集合S,式子的前半部分即为它对答案贡献的系数。
具体证明看大佬的文章
状压dp
#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))typedef long long LL;const int N=55,mo=1000003;int ans,len,n,K;char cc[N];int a[N][N];int f[N][100100],er[N],p[100100],b[N][N];int main(){ er[0]=1; for(int i=1;i<=20;i++) er[i]=er[i-1]*2; cin>>n>>K; for(int i=1;i<=n;i++) { scanf("%s",&cc); len=strlen(cc); for(int j=1;j<=len;j++) if(cc[j-1]=='?') a[i][j]=26; else a[i][j]=cc[j-1]-'a'; } int hy=er[n]-1; f[0][hy]=1; for(int i=1;i<=hy;i++) p[i]=p[i-(i&-i)]+1; for(int i=1;i<=len;i++) for(int j=0;j<=25;j++) for(int k=1;k<=n;k++) if(a[k][i]==26||a[k][i]==j) b[i][j]|=er[k-1]; for(int i=0;i<len;i++) for(int j=1;j<=hy;j++) if(p[j]>=K&&f[i][j]!=0) for(int k=0;k<=25;k++) f[i+1][(j&b[i+1][k])]=(f[i+1][(j&b[i+1][k])]+f[i][j])%mo; for(int j=1;j<=hy;j++) if(p[j]==K) ans=(ans+f[len][j])%mo; cout<<ans<<endl; return 0;}
广义容斥原理
#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))typedef long long LL;const int N=55;const LL mo=1000003;int n,K,len;int er[N],q[100100];LL f[N],C[N][N],ans,p[N];char c[N][N],a[N];int main(){ C[0][0]=1; for(int i=1;i<=20;i++) { C[i][0]=C[i][i]=1; for(int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mo; } er[0]=p[0]=1; for(int i=1;i<=20;i++) er[i]=er[i-1]*2; for(int i=1;i<=50;i++) p[i]=p[i-1]*26%mo; cin>>n>>K; for(int i=1;i<=n;i++) scanf("%s",c[i]); len=strlen(c[1]); int hy=(1<<n)-1; for(int i=1;i<=hy;i++) q[i]=q[i-(i&-i)]+1; for(int i=1;i<=hy;i++) { int now=len; bool ok=true; for(int j=0;j<len;j++) a[j]='?'; for(int j=1;j<=n;j++) if(er[j-1]&i) { for(int k=0;k<len;k++) if(c[j][k]!='?') { if(a[k]!='?'&&c[j][k]!=a[k]) ok=false; if(a[k]=='?') now--; a[k]=c[j][k]; } } if(!ok) continue; if(q[i]>=K) { if((q[i]-K)%2==0) ans=(ans+C[q[i]][K]*p[now])%mo; else ans=(ans+mo-C[q[i]][K]*p[now]%mo)%mo; } } cout<<ans<<endl; return 0;}
阅读全文
0 0
- smoj2075:匹配数(状压dp/广义容斥原理)
- 广义容斥原理
- 广义容斥原理
- 容斥的原理及广义应用
- bzoj 3812 状压dp 容斥原理
- bzoj 2669 状压dp 容斥原理
- bzoj 3294: [Cqoi2011]放棋子 (容斥原理+组合数+DP)
- [jzoj]4086. 选数(number)(容斥+DP优化)
- BZOJ 3812 主旋律 状压DP+容斥原理
- 4455: [Zjoi2016]小星星|状压DP|容斥原理
- [BZOJ2669][cqoi2012][状压DP][容斥原理]局部极小值
- [HDU 5731]Solid Dominoes Tilings:状压DP+容斥原理
- 【BZOJ3812】【状压DP】【容斥原理】主旋律 题解
- hdu4336 Card Collector 概率dp(或容斥原理?)
- hdu 4336 Card Collector (期望dp|容斥原理)
- BZOJ 1042(简单DP+容斥原理)
- 1042: [HAOI2008]硬币购物(dp+容斥原理)
- 【bzoj 1042】 [HAOI2008] 硬币购物(dp+容斥原理)
- Android ContentProvider内容提供者
- 底层jdbc查询操作
- C++项目总四之内存溢出造成的诡异函数调用
- 图论作业10.9
- 单客户端与服务器通信
- smoj2075:匹配数(状压dp/广义容斥原理)
- javascript之原型与原型链、执行上下文与执行上下文栈
- 我的JS经典题目合集
- Jupyter Notebook 27绝技
- [NOIP模拟][好题]分玩具
- Hololens开发之项目一: Holograms 100
- HDU 2059 龟兔赛跑(线性DP)
- 软件架构模式
- 个性化推荐的十大挑战