Bipartite 随机二分图 bzoj 5006
题目描述
某人在玩一个非常神奇的游戏。这个游戏中有一个左右各 nn 个点的二分图,图中的边会按照一定的规律随机出现。
为了描述这些规律,某人将这些边分到若干个组中。每条边或者不属于任何组 (这样的边一定不会出现),或者只属于一个组。
有且仅有以下三类边的分组:
-
这类组每组只有一条边,该条边恰好有 50\%50% 的概率出现。
-
这类组每组恰好有两条边,这两条边有 50\%50% 的概率同时出现,有 50\%50% 的概率同时不出现。
- 这类组每组恰好有两条边,这两条边恰好出现一条,各有 50\%50% 的概率出现。
组和组之间边的出现都是完全独立的。
某人现在知道了边的分组和组的种类,想要知道完美匹配数量的期望是多少。你能帮助她解决这个问题吗?
定义解释
如果你对完美匹配和期望的定义很熟悉,那么你可以跳过本段。
对于一个左右各 nn 个点的二分图,它的一个完美匹配是指 nn 条没有公共点的边构成的匹配。
两个完美匹配不同,当且仅当它们至少含有一条不同的边。一个二分图完美匹配的数量定义为这张图能找到的两两不同的完美匹配的数量。
在题目的图中,边都是随机出现的,因此这个图中完美匹配的数量是一个随机变量。一个(离散型)随机变量 XX 的期望定义为以概率为权,XX 所有可能取值的加权平均数,即
其中 V(X)V(X) 表示 XX 所有可能的取值集合,P[X=x]P[X=x] 表示 XX 取值为 xx 的概率。
输入格式
从标准输入读入数据。
第一行两个数 nn 和 mm,表示图左右点数的数量和边的组的个数。我们用 (a,b)(a,b) (其中 1 \le a,b \le n1≤a,b≤n)表示一条左端点为二分图左侧第 aa 个点,右端点为二分图右侧第 bb 个点的边。
接下来 mm 行,每行描述一个组。开头第一个数 tt 表示组的种类,t=0t=0 表示是一条边的组,t=1t=1 表示是两条边的组中的第一种,t=2t=2 表示是两条边的组中的第二种。如果 t=0t=0, 接下来两个数 a_1,b_1a1,b1 表示组内的第一条边;否则,接下来四个数 a_1,b_1,a_2,b_2a1,b1,a2,b2, 表示该组内的两条边分别为 (a_1,b_1)(a1,b1) 和 (a_2,b_2)(a2,b2)。保证每条边至多出现一次。
输出格式
输出到标准输出。
假设期望的完美匹配数量是EE。输出一行表示
可以看出上式一定是一个整数。
样例
样例输入1
2 2
1 2 1 2 2
2 1 2 1 1
样例输出1
2
样例输入2
3 5
1 1 2 3 3
1 3 2 2 2
1 1 1 1 3
1 2 1 3 1
0 2 3
样例输出2
7
数据范围与提示
对于 5\%5% 的数据 n \le 5n≤5 。
对于另 5\%5% 的数据 n \le 8n≤8 。
对于另 10\%10% 的数据 n \le 10n≤10 。
对于另 15\%15% 的数据,只有t = 0t=0 的情况。
对于另 5\%5% 的数据,只有t = 0t=0 的情况,且m = n^2m=n2,也就是该图为一个完全图。
对于另 20\%20% 的数据,只有 t =0t=0 或者 t=1t=1 的情况。
对于另 20\%20% 的数据,只有 t =0t=0 或者 t=2t=2 的情况。
对于 100\%100% 的数据,n \le 15n≤15。
思路:
注意数据范围是n ≤ 15.
可以状压,把 2n 个点的匹配情况压到一个int里去。
然后dp.
接下来我们要处理每个边组。
对于类型为0的边组,直接建边。
对于类型为1的边组(a,b)(c,d),我们可以先建两条概率为50%的边,再建一个概率为25%的边组。
对于类型为2的边组(a,b)(c,d),我们可以先建两条概率为50%的边,再建一个概率为-25%的边组。
具体怎么实现?把边和边组也压成int,转移时直接异或(^)即可。
我们发现这样的代码会TLE.
怎么办呢?
对于一个状态S,转移的时候强制要转移走它的最低位(lowbit).
然后我们存不下2^2N的f数组,所以我们用map,再加上记忆化搜索即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define rep(i,a,b) for(R i=a;i<=b;i++) 5 #define Rep(i,a,b) for(R i=a;i>=b;i--) 6 #define ms(i,a) memset(a,i,sizeof(a)) 7 #define gc() getchar() 8 #define LL long long 9 template<class T>void read(T &x){ 10 x=0; char c=0; 11 while (!isdigit(c)) c=gc(); 12 while (isdigit(c)) x=x*10+(c^48),c=gc(); 13 } 14 int const N=16; 15 int const M=250; 16 int const mod=1e9+7; 17 struct node{ 18 int w,S; 19 }a[40][M],tmp; 20 LL inv2,inv4,inv44,n,m,cnt[40]; 21 map<int,LL>f[1<<N]; 22 LL ksm(LL x,LL y){ 23 LL ret=1; while (y) { if(y&1) ret=ret*x%mod; x=x*x%mod;y>>=1; }return ret; 24 } 25 void add(){ 26 rep(i,0,n-1) if((1<<i) & tmp.S){ 27 a[i][++cnt[i]]=tmp; return ; 28 } 29 } 30 31 LL solve(LL S){ 32 int s=S&((1<<n)-1),t=S>>n,p; 33 if(!S) return 1; 34 if(f[s].count(t)) return f[s][t]; 35 LL ans=0; 36 rep(i,0,n-1) if(S & (1<<i)) { p=i; break;} 37 rep(i,1,cnt[p]) if( (S&a[p][i].S)==a[p][i].S) ans=(ans+solve(S^a[p][i].S)*a[p][i].w%mod) %mod; 38 return f[s][t]=ans; 39 } 40 41 int main(){ 42 inv2=ksm(2,mod-2); 43 inv4=ksm(4,mod-2); 44 inv44=ksm( mod-4,mod-2); 45 read(n); read(m); 46 while (m--){ 47 int type,u1,v1,u2,v2; read(type); 48 read(u1);read(v1); u1--,v1--; 49 if(!type){ 50 tmp.S=(1<<u1)| (1<<(n+v1)); tmp.w=inv2;add(); 51 } 52 if(type==1){ 53 read(u2); read(v2) ; u2--;v2--; 54 LL s1=(1<<u1)|(1<<(v1+n)); 55 LL s2=(1<<u2)|(1<<(v2+n)); 56 tmp.S=s1;tmp.w=inv2; add(); 57 tmp.S=s2;tmp.w=inv2; add(); 58 if(s1&s2) continue; 59 tmp.S=s1|s2; tmp.w=inv4; add(); 60 } 61 if(type==2){ 62 read(u2);read(v2); u2--;v2--; 63 LL s1=(1<<u1)|(1<<v1+n); 64 LL s2=(1<<u2)|(1<<v2+n); 65 tmp.S=s1;tmp.w=inv2; add(); 66 tmp.S=s2;tmp.w=inv2; add(); 67 if(s1&s2) continue; 68 tmp.S=s1|s2; tmp.w=inv44; add(); 69 } 70 } 71 LL ans=solve((1LL<<(2*n))-1) * (1<<n) % mod; 72 cout<<ans<<endl; 73 return 0; 74 }