随机二分图

题目描述

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

为了描述这些规律,某人将这些边分到若干个组中。每条边只属于一个组。

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

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

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

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

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

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

输入格式

从标准输入读入数据。

第一行两个数 \(n\)\(m\),表示图左右点数的数量和边的组的个数。我们用 \((a,b)\) (其中 \(1\le a,b\le n\))表示一条左端点为二分图左侧第 \(a\) 个点,右端点为二分图右侧第 \(b\) 个点的边。

接下来 \(m\) 行,每行描述一个组。开头第一个数 \(t\) 表示组的种类,\(0\) 表示是一条边的组,\(2\) 表示是两条边的组中的第一种,\(2\) 表示是两条边的组中的第二种。如果 \(t=0\), 接下来两个数 \(a,b\) 表示组内的第一条边;否则,接下来四个数 \(a1,ba,a2,b2\), 表示该组内的两条边分别为 \((a1,b1)\)\((a2,b2)\)

保证每条边至多出现一次。

输出格式

输出到标准输出。

假设期望的完美匹配数量是 \(E\)。输出一行表示
\[(2^nE)\ mod\ (1e9+7)\]

可以看出上式一定是一个整数。

其实这题不是很难吧(只要你抱着骗分的心态去做)

看到 \(n\le 15\) 肯定能想到状压,但是状压 \(2^{2n}\) 复杂度不对啊? 那就别循环了,直接记搜

先考虑 \(t=0\) 的情况

由于每个点一定需要匹配一条边,直接扫每个左边的点,同时枚举他的出边选那一条就行了

然后考虑 \(t=1/2\)

\(t=1\) 时,如果我们像 \(t=0\) 一样枚举出边且每次概率乘以 \(\frac{1}{2}\),发现当同时选择 \((a1,b1)\)\((a2,b2)\) 时贡献只有 \(\frac{1}{4}\)

同理当 \(t=2\) 时,当同时选择 \((a1,b1)\)\((a2,b2)\) 时贡献多了 \(\frac{1}{4}\)

考虑怎么补回来这些损失和花费

如果我们有一种情况中同时选择了 \((a1,b1)\)\((a2,b2)\) ,那么如果建出一条 \((a1,b1),(a2,b2)\) 的边,一定会在某种情况被选上

那么对于 \(t=1\),我们建出一条 \((a1,b1)·(a2,b2)\) 的边,使它的概率为 \(\frac{1}{4}\),这样可以补回来少的 \(\frac{1}{4}\)

对于 \(t=2\),我们建出一条 \((a1,b1)·(a2,b2)\) 的边,使它的概率为 \(-\frac{1}{4}\),这样可以加上多加的 \(\frac{1}{4}\)

然后记搜就行了

code

#include<bits/stdc++.h>
#define ll long long
#define cri const register int
#define re register
using namespace std;
const int mod=1e9+7,inv2=500000004,inv4=250000002;
vector<int>is[16],e[16];
unordered_map<int,int>mp;
int s[40000];
int dfs(cri now){
    if(!now) return 1;
    if(mp.find(now)!=mp.end()) return mp[now];
    int p=s[now&-now],ans=0;
    for(int i=0;i<is[p].size();i++)
        if((now&is[p][i])==is[p][i])
            ans=(ans+1ll*dfs(now^is[p][i])*e[p][i]%mod)%mod;
    return mp[now]=ans;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) s[1<<i-1]=i;
    while(m--){
        int opt,a1,a2,b1,b2;
        scanf("%d",&opt);
        if(!opt){
            scanf("%d%d",&a1,&b1);
            int tmp=(1<<a1-1)|(1<<b1+n-1);
            is[a1].push_back(tmp);
            e[a1].push_back(inv2);
        }
        if(opt==1){
            scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
            int tmp1=(1<<a1-1)|(1<<b1+n-1),tmp2=(1<<a2-1)|(1<<b2+n-1),tmp=tmp1|tmp2;
            is[a1].push_back(tmp1);e[a1].push_back(inv2);
            is[a2].push_back(tmp2);e[a2].push_back(inv2);
            if(a1!=a2&&b1!=b2){
                is[a1].push_back(tmp);e[a1].push_back(inv4);
                is[a2].push_back(tmp);e[a2].push_back(inv4);
            }
        }
        if(opt==2){
            scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
            int tmp1=(1<<a1-1)|(1<<b1+n-1),tmp2=(1<<a2-1)|(1<<b2+n-1),tmp=tmp1|tmp2;
            is[a1].push_back(tmp1);e[a1].push_back(inv2);
            is[a2].push_back(tmp2);e[a2].push_back(inv2);
            if(a1!=a2&&b1!=b2){
                is[a1].push_back(tmp);e[a1].push_back(mod-inv4);
                is[a2].push_back(tmp);e[a2].push_back(mod-inv4);
            }
        }
    }
    int ans=dfs((1<<n+n)-1);
    for(int i=1;i<=n;i++) ans=ans*2%mod;
    cout<<ans<<endl;
}

猜你喜欢

转载自www.cnblogs.com/mikufun-hzoi-cpp/p/12147050.html