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的元素个数。公式为

f[r]=SP(1)g[S]rCrg[S]w[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;}

这里写图片描述

原创粉丝点击