[THUWC2017]随机二分图

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;
}
相关文章
相关标签/搜索