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; }