https://www.zybuluo.com/ysner/note/1242342
##題面 某人在玩一個很是神奇的遊戲。這個遊戲中有一個左右各$n$個點的二分圖,圖中的邊會按照必定的規律隨機出現。 爲了描述這些規律,某人將這些邊分到若干個組中。每條邊只屬於一個組。 有且僅有如下三類邊的分組:(用$t$表示)node
- 這類組每組只有一條邊,該條邊剛好有$50%$的機率出現。
- 這類組每組剛好有兩條邊,這兩條邊有$50%$的機率同時出現,有$50%$ 的機率同時不出現。
- 這類組每組剛好有兩條邊,這兩條邊剛好出現一條,各有$50%$的機率出現。
組和組之間邊的出現都是徹底獨立的。 某人如今知道了邊的分組和組的種類,詢問完美匹配數量的指望是多少,輸出$2^nE$($E$指指望)ios
- $20pts\ n\leq10$
- $5pts\ t=0,m=n^2$
- $15pts\ t=0$
- $100pts\ n\leq15$ ##解析 其實沒有注意到某$5pts$的選手仍是很。。。
能夠把題目轉化一下,應用映射思想,把左邊點一一配對的右邊點的順序視做一個序列。(容許不配對) 如左$1$配右$2$,左$2$配右$4$,左$3$不配,左$4$配右$1$。 造成序列爲$24_1$。 ###$5pts\ m=n^2$ 很顯然這個(映射)序列能夠是全排列。 邊的總方案數$2^m$,完美匹配方案數$n!2^{m-n}$,則$$E=\frac{n!2^{m-n}}{2^m}=\frac{n!}{2^n}$$ 最後$ans=n!$ ###$20pts$算法 咱們能夠枚舉一下點完美匹配的方案,再統計方案數。算法
可是怎麼反映邊與邊之間的關係呢? 這一點能夠聯想一下網絡流的技巧:拆點。 第一類不用說。 第二類兩條邊的編號相同。 第三類兩條邊的編號差$m$。 依此,在檢驗合法性時,若$i$與$i+m$同時存在即不合法; 在統計方案數時,在該匹配下$i$與$i+m$均未用到,則該組邊出不出現皆可,該匹配下方案數$*2$。網絡
最後,邊出現的總方案數爲$2^m$,完美匹配方案數爲$ans$,則指望爲$E=\frac{ans}{2^m}$ 最終答案爲$$\frac{ans*2^n}{2^m}$$ 複雜度$O(n!m)$url
il void check() { memset(b,0,sizeof(b)); fp(i,1,n) b[p[i]]=1; re ll sum=1; fp(i,1,m) { if(b[i]&&b[i+m]) return; if(!b[i]&&!b[i+m]) (sum*=2)%=mod; } (ans+=sum)%=mod; } il void dfs(re int x) { if(x>n) {check();return;} fp(i,1,n) if(!vis[i]&&id[x][i]) { p[x]=id[x][i];vis[i]=1; dfs(x+1); vis[i]=0; } } int main() { n=gi();m=gi(); if(m==n*n) { ans=1; fp(i,1,n) (ans*=i)%=mod; printf("%lld\n",ans); return 0; } if(n<=10) { fp(i,1,m) { re int t=gi(),u=gi(),v=gi(),u1,v1; if(t) u1=gi(),v1=gi(); if(t==0) id[u][v]=i; if(t==1) id[u][v]=id[u1][v1]=i; if(t==2) id[u][v]=i,id[u1][v1]=i+m; } dfs(1); printf("%lld\n",ans*ksm(ksm(2,m),mod-2)%mod*ksm(2,n)%mod); } return 0; }
###$40pts$算法 其實我不太會??? ###$100pts$算法 把雙邊組看作互不干擾的、出現機率爲$50%$的邊。若是這樣,第二類組合邊出現機率會少算 $25%$(同時出現的機率爲$25%$),第三類組合邊出現機率會多算$25%$(只出現一條的機率爲$50%$)。咱們能夠分別爲這兩組建$+25%,-25%$的邊將這個機率抵消(由於各組獨立)。spa
舉個例子,在第二類中,兩條邊同時出現的機率爲$50%*50%+25%=50%$,不一樣時出現的機率爲$1-25%-(50%*50%)=50%$,符合要求。.net
而後記憶化搜索,$f[S]$表示$S$集合內的點的完美匹配的指望方案數,爲了保證選邊的有序性並同時減小狀態數,$f[S]$由$f[S1]$轉移過來時,要求$S$最高位比$S1$的最高位高。code
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<map> #define re register #define il inline #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define fp(i,a,b) for(re int i=a;i<=b;i++) #define fq(i,a,b) for(re int i=a;i>=b;i--) using namespace std; const int mod=1e9+7,N=10000,inv2=mod+1>>1,inv4=mod+1>>2; struct node { int s,w; il node(){s=w=0;} il node(re int x,re int y){s=x,w=y;} }a[N]; int tot,n,m; map<int,int>f[1<<16]; il int gi() { re int x=0,t=1; re char ch=getchar(); while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') t=-1,ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar(); return x*t; } il int dfs(re int S) { if(!S) return 1; re int T0=S>>n,S0=S^(T0<<n); if(f[S0].count(T0)) return f[S0][T0]; re int &tmp=f[S0][T0]; fp(i,1,tot) { re int T=a[i].s; if((T&S)==T&&S<(T<<1)) (tmp+=1ll*dfs(S^T)*a[i].w%mod)%=mod; } return tmp; } int main() { n=gi();m=gi(); fp(i,1,m) { re int t=gi(),u=gi(),v=gi(),u1,v1; re int S1=(1<<(u-1))|(1<<(v+n-1)); a[++tot]=node(S1,inv2); if(t) { u1=gi(),v1=gi(); re int S2=(1<<u1-1)|(1<<v1+n-1); a[++tot]=node(S2,inv2); if(S1&S2) continue; if(t==1) a[++tot]=node(S1|S2,inv4); if(t==2) a[++tot]=node(S1|S2,mod-inv4); } } //printf("%d\n",tot); printf("%lld\n",(1ll<<n)*dfs((1ll<<(2*n))-1)%mod); return 0; }