[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;
}
相關文章
相關標籤/搜索