BZOJ5006 THUWC2017随机二分图(概率期望+状压dp)

只谈情不闲聊 提交于 2020-04-04 21:13:07

  下称0类为单边,1类为互生边,2类为互斥边。对于一种匹配方案,考虑其出现的概率*2n后对答案的贡献,初始为1,如果有互斥边显然变为0,否则每有一对互生边其贡献*2。于是有一个显然的dp,即设f[S1][S2]为左边选取S1右边选取S2对答案的贡献。转移时考虑S1中编号最小的点x与右边的点y匹配。首先将f[S1-(1<<x)][S2-(1<<y)]统计进去。然后若(x,y)是单边,或者虽存在互生互斥关系,但其对应边的左端点还不在S1中或就是x,或右端点还不在S2中或就是y,就不管了;否则若互斥将f[S1-(1<<x)-(1<<x')][S2-(1<<y)-(1<<y')]减掉,若互生将f[S1-(1<<x)-(1<<x')][S2-(1<<y)-(1<<y')]加上,其中(x',y')是(x,y)的对应边。这样大概就是C(30,15)*15的。

  但是这只有暴力20分,甚至连空间都开不下。然而满分做法和他是一个复杂度的。对上面的做法改为记忆化搜索,map存储状态就可以了。多了log也多了80分。不是非常理解意义何在。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
#define ll long long
#define N 15
#define P 1000000007
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,m,typ[N][N],match[N][N][2],s[1<<N];
map<int,int> f;
void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
inline int trans(int n,int m){return n<<N|m;}
int solve(int i,int j)
{
    if (f.find(trans(i,j))!=f.end()) return f[trans(i,j)];
    int x=i&-i,ans=0;
    for (int t=j,k=t&-t;t;t^=k,k=t&-t)
    if ((k&j)&&typ[s[x]][s[k]]>=0)
    {
        inc(ans,solve(i^x,j^k));
        int u=match[s[x]][s[k]][0],v=match[s[x]][s[k]][1];
        if ((u&i)&&(v&j)&&x!=u&&k!=v)
        {
            if (typ[s[x]][s[k]]==1) inc(ans,solve(i^x^u,j^k^v));
            if (typ[s[x]][s[k]]==2) inc(ans,P-solve(i^x^u,j^k^v));
        }
    }
    f[trans(i,j)]=ans;return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5006.in","r",stdin);
    freopen("bzoj5006.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),m=read();
    memset(typ,255,sizeof(typ));
    for (int i=1;i<=m;i++)
    {
        int op=read(),x=read()-1,y=read()-1;
        if (op==0) typ[x][y]=0;
        else
        {
            int p=read()-1,q=read()-1;
            typ[x][y]=typ[p][q]=op;
            match[x][y][0]=1<<p,match[x][y][1]=1<<q;
            match[p][q][0]=1<<x,match[p][q][1]=1<<y;
        }
    }
    for (int i=0;i<n;i++) s[1<<i]=i;
    f[0]=1;
    cout<<solve((1<<n)-1,(1<<n)-1);
    return 0;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!