ccz181078

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

某人在玩一个非常神奇的游戏。这个游戏中有一个左右各 nnn 个点的二分图,图中的边会按照一定的规律随机出现。

为了描述这些规律,某人将这些边分到若干个组中。每条边或者不属于任何组 (这样的边一定不会出现),或者只属于一个组。

有且仅有以下三类边的分组:

  1. 这类组每组只有一条边,该条边恰好有 50%50\%50% 的概率出现。

  2. 这类组每组恰好有两条边,这两条边有 50%50\%50% 的概率同时出现,有 50%50\%50% 的概率同时不出现。

  3. 这类组每组恰好有两条边,这两条边恰好出现一条,各有 50%50\%50% 的概率出现。

组和组之间边的出现都是完全独立的。

某人现在知道了边的分组和组的种类,想要知道完美匹配数量的期望是多少。你能帮助她解决这个问题吗?

把两条边一组的暂时看作两条边分别有1/2概率出现,这导致两条边同时出现在匹配中时的计算有误差,可以将误差用 一组两条边有±1/4概率同时出现 抵消,然后记忆化搜索,f[S]表示S集合内的点的完美匹配的期望方案数,为了保证选边的有序性并同时减少状态数,f[S]由f[S1]转移过来时,要求S最高位比S1的最高位高

#include<bits/stdc++.h>
typedef long long i64;
const int M=(1<<21)-1,P=1e9+7,I2=(P+1)/2,I4=I2/2;
int n,m,ep=0,_;
int h[M+111][2],rnd[1111];
int ht=0;
int&get(int x){
    _=1;
    int w=(x&M)^rnd[x>>21];
    while(h[w][0]){
        if(h[w][0]==x)return h[w][1];
        w=w+1237&M;
    }
    _=0;
    h[w][0]=x;
    return h[w][1];
}
struct edge{
    int S,p,c;
    void upd(int&f,int S0,int c0){
        int F(int,int);
        if((S0&S)==S&&S0<(S<<1)){
            f=(f+F(S0^S,c0-c)*i64(p)%P)%P;
        }
    }
}e[607];
int F(int S,int c){
    if(!S)return 1;
    int&f=get(S);
    if(!_)for(int i=0;i<ep;++i)e[i].upd(f,S,c);
    return f;
}
int main(){
    scanf("%d%d",&n,&m);
    srand(n+m+112);
    for(int i=0;i<1024;++i)rnd[i]=(rand()^rand()<<11)&M;
    for(int i=0,tp,a,b,c,d;i<m;++i){
        scanf("%d",&tp);
        scanf("%d%d",&a,&b);
        int e1=1<<a-1|1<<n+b-1;
        e[ep++]=(edge){e1,I2,1};
        if(tp){
            scanf("%d%d",&c,&d);
            int e2=1<<c-1|1<<n+d-1;
            e[ep++]=(edge){e2,I2,1};
            if(!(e1&e2))e[ep++]=(edge){e1|e2,tp==1?I4:-I4,2};
        }
    }
    int ans=(i64((F((1<<n*2)-1,n)+P)%P)<<n)%P;
    printf("%d\n",ans);
    return 0;
}

 

posted on 2017-09-10 17:03  nul  阅读(909)  评论(0编辑  收藏  举报