随机二分图
题目描述
某人在玩一个非常神奇的游戏。这个游戏中有一个左右各 n 个点的二分图,图中的边会按照一定的规律随机出现。
为了描述这些规律,某人将这些边分到若干个组中。每条边只属于一个组。
有且仅有以下三类边的分组:
1.这类组每组只有一条边,该条边恰好有 50% 的概率出现。
2.这类组每组恰好有两条边,这两条边有 50% 的概率同时出现,有 50% 的概率同时不出现。
3.这类组每组恰好有两条边,这两条边恰好出现一条,各有 50% 的概率出现。
组和组之间边的出现都是完全独立的。
某人现在知道了边的分组和组的种类,想要知道完美匹配数量的期望是多少。你能帮助她解决这个问题吗?
输入格式
从标准输入读入数据。
第一行两个数 n 和 m,表示图左右点数的数量和边的组的个数。我们用 (a,b) (其中 1≤a,b≤n)表示一条左端点为二分图左侧第 a 个点,右端点为二分图右侧第 b 个点的边。
接下来 m 行,每行描述一个组。开头第一个数 t 表示组的种类,0 表示是一条边的组,2 表示是两条边的组中的第一种,2 表示是两条边的组中的第二种。如果 t=0, 接下来两个数 a,b 表示组内的第一条边;否则,接下来四个数 a1,ba,a2,b2, 表示该组内的两条边分别为 (a1,b1) 和 (a2,b2)。
保证每条边至多出现一次。
输出格式
输出到标准输出。
假设期望的完美匹配数量是 E。输出一行表示
可以看出上式一定是一个整数。
其实这题不是很难吧(只要你抱着骗分的心态去做)
看到 n≤15 肯定能想到状压,但是状压 22n 复杂度不对啊? 那就别循环了,直接记搜
先考虑 t=0 的情况
由于每个点一定需要匹配一条边,直接扫每个左边的点,同时枚举他的出边选那一条就行了
然后考虑 t=1/2
t=1 时,如果我们像 t=0 一样枚举出边且每次概率乘以 12,发现当同时选择 (a1,b1) 和 (a2,b2) 时贡献只有 14
同理当 t=2 时,当同时选择 (a1,b1) 和 (a2,b2) 时贡献多了 14
考虑怎么补回来这些损失和花费
如果我们有一种情况中同时选择了 (a1,b1) 和 (a2,b2) ,那么如果建出一条 (a1,b1),(a2,b2) 的边,一定会在某种情况被选上
那么对于 t=1,我们建出一条 (a1,b1)·(a2,b2) 的边,使它的概率为 14,这样可以补回来少的 14
对于 t=2,我们建出一条 (a1,b1)·(a2,b2) 的边,使它的概率为 −14,这样可以加上多加的 14
然后记搜就行了
复杂度什么的不重要就当$(能过)吧
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?