zoukankan      html  css  js  c++  java
  • [THUWC2017]随机二分图

    https://www.zybuluo.com/ysner/note/1242342

    题面

    某人在玩一个非常神奇的游戏。这个游戏中有一个左右各(n)个点的二分图,图中的边会按照一定的规律随机出现。
    为了描述这些规律,某人将这些边分到若干个组中。每条边只属于一个组。
    有且仅有以下三类边的分组:(用(t)表示)

    • 这类组每组只有一条边,该条边恰好有(50\%)的概率出现。
    • 这类组每组恰好有两条边,这两条边有(50\%)的概率同时出现,有(50\%) 的概率同时不出现。
    • 这类组每组恰好有两条边,这两条边恰好出现一条,各有(50\%)的概率出现。

    组和组之间边的出现都是完全独立的。
    某人现在知道了边的分组和组的种类,询问完美匹配数量的期望是多少,输出(2^nE)(E)指期望)

    • (20pts nleq10)
    • (5pts t=0,m=n^2)
    • (15pts t=0)
    • (100pts nleq15)

    解析

    其实没有注意到某(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))

    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
    ",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
    ",ans*ksm(ksm(2,m),mod-2)%mod*ksm(2,n)%mod);
        }
      return 0;
    }
    

    (40pts)算法

    其实我不太会???

    (100pts)算法

    把双边组看做互不干扰的、出现概率为(50\%)的边。如果这样,第二类组合边出现概率会少算
    (25\%)(同时出现的概率为(25\%)),第三类组合边出现概率会多算(25\%)(只出现一条的概率为(50\%))。我们可以分别为这两组建(+25\%,-25\%)的边将这个概率抵消(因为各组独立)。

    举个例子,在第二类中,两条边同时出现的概率为(50\%*50\%+25\%=50\%),不同时出现的概率为(1-25\%-(50\%*50\%)=50\%),符合要求。

    然后记忆化搜索,(f[S])表示(S)集合内的点的完美匹配的期望方案数,为了保证选边的有序性并同时减少状态数,(f[S])(f[S1])转移过来时,要求(S)最高位比(S1)的最高位高

    #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
    ",tot);
      printf("%lld
    ",(1ll<<n)*dfs((1ll<<(2*n))-1)%mod);
      return 0;
    }
    
  • 相关阅读:
    AJAX
    Django(cookie和session)
    Django(分页)
    跨站请求伪造和csrf_token使用
    Django(ORM查询联系题)
    Django(ORM查询2)
    Django(ORM查询1)
    Django(ORM常用字段)
    复习
    第二次作业:卷积神经网络 part2
  • 原文地址:https://www.cnblogs.com/yanshannan/p/9452802.html
Copyright © 2011-2022 走看看