[关闭]
@ysner 2018-08-10T07:53:31.000000Z 字数 2938 阅读 2505

[THUWC2017]随机二分图

计数 期望 DP


题面

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

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

解析

其实没有注意到某的选手还是很。。。

可以把题目转化一下,应用映射思想,把左边点一一配对的右边点的顺序视作一个序列。(允许不配对
如左配右,左配右,左不配,左配右
形成序列为

很显然这个(映射)序列可以是全排列。
边的总方案数,完美匹配方案数,则


最后

算法

我们可以枚举一下点完美匹配的方案,再统计方案数。

但是怎么反映边与边之间的关系呢?
这一点可以联想一下网络流的技巧:拆点。
第一类不用说。
第二类两条边的编号相同。
第三类两条边的编号差
依此,在检验合法性时,若同时存在即不合法;
在统计方案数时,在该匹配下均未用到,则该组边出不出现皆可,该匹配下方案数

最后,边出现的总方案数为,完美匹配方案数为,则期望为
最终答案为


复杂度

  1. il void check()
  2. {
  3. memset(b,0,sizeof(b));
  4. fp(i,1,n) b[p[i]]=1;
  5. re ll sum=1;
  6. fp(i,1,m)
  7. {
  8. if(b[i]&&b[i+m]) return;
  9. if(!b[i]&&!b[i+m]) (sum*=2)%=mod;
  10. }
  11. (ans+=sum)%=mod;
  12. }
  13. il void dfs(re int x)
  14. {
  15. if(x>n) {check();return;}
  16. fp(i,1,n)
  17. if(!vis[i]&&id[x][i])
  18. {
  19. p[x]=id[x][i];vis[i]=1;
  20. dfs(x+1);
  21. vis[i]=0;
  22. }
  23. }
  24. int main()
  25. {
  26. n=gi();m=gi();
  27. if(m==n*n)
  28. {
  29. ans=1;
  30. fp(i,1,n) (ans*=i)%=mod;
  31. printf("%lld\n",ans);
  32. return 0;
  33. }
  34. if(n<=10)
  35. {
  36. fp(i,1,m)
  37. {
  38. re int t=gi(),u=gi(),v=gi(),u1,v1;
  39. if(t) u1=gi(),v1=gi();
  40. if(t==0) id[u][v]=i;
  41. if(t==1) id[u][v]=id[u1][v1]=i;
  42. if(t==2) id[u][v]=i,id[u1][v1]=i+m;
  43. }
  44. dfs(1);
  45. printf("%lld\n",ans*ksm(ksm(2,m),mod-2)%mod*ksm(2,n)%mod);
  46. }
  47. return 0;
  48. }

算法

其实我不太会???

算法

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

举个例子,在第二类中,两条边同时出现的概率为,不同时出现的概率为,符合要求。

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

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #include<map>
  8. #define re register
  9. #define il inline
  10. #define ll long long
  11. #define max(a,b) ((a)>(b)?(a):(b))
  12. #define min(a,b) ((a)<(b)?(a):(b))
  13. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int mod=1e9+7,N=10000,inv2=mod+1>>1,inv4=mod+1>>2;
  17. struct node
  18. {
  19. int s,w;
  20. il node(){s=w=0;}
  21. il node(re int x,re int y){s=x,w=y;}
  22. }a[N];
  23. int tot,n,m;
  24. map<int,int>f[1<<16];
  25. il int gi()
  26. {
  27. re int x=0,t=1;
  28. re char ch=getchar();
  29. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  30. if(ch=='-') t=-1,ch=getchar();
  31. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  32. return x*t;
  33. }
  34. il int dfs(re int S)
  35. {
  36. if(!S) return 1;
  37. re int T0=S>>n,S0=S^(T0<<n);
  38. if(f[S0].count(T0)) return f[S0][T0];
  39. re int &tmp=f[S0][T0];
  40. fp(i,1,tot)
  41. {
  42. re int T=a[i].s;
  43. if((T&S)==T&&S<(T<<1)) (tmp+=1ll*dfs(S^T)*a[i].w%mod)%=mod;
  44. }
  45. return tmp;
  46. }
  47. int main()
  48. {
  49. n=gi();m=gi();
  50. fp(i,1,m)
  51. {
  52. re int t=gi(),u=gi(),v=gi(),u1,v1;
  53. re int S1=(1<<(u-1))|(1<<(v+n-1));
  54. a[++tot]=node(S1,inv2);
  55. if(t)
  56. {
  57. u1=gi(),v1=gi();
  58. re int S2=(1<<u1-1)|(1<<v1+n-1);
  59. a[++tot]=node(S2,inv2);
  60. if(S1&S2) continue;
  61. if(t==1) a[++tot]=node(S1|S2,inv4);
  62. if(t==2) a[++tot]=node(S1|S2,mod-inv4);
  63. }
  64. }
  65. //printf("%d\n",tot);
  66. printf("%lld\n",(1ll<<n)*dfs((1ll<<(2*n))-1)%mod);
  67. return 0;
  68. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注