BZOJ 5006 [THUWC2017]Bipartite 随机二分图

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=5006

题解

首先假设只有第一种情况 ( t = 0 ) (t=0) (t=0),记 f [ i ] [ j ] f[i][j] f[i][j]表示左边的匹配状态为 i i i,右边匹配状态为 j j j时,匹配方案的期望。转移枚举左边的最高位匹配到了哪条边,这样可以避免重复转移。

考虑第二种情况 ( t = 1 ) (t=1) (t=1),设两条边分别为 ( x , y ) , ( a , b ) (x,y),(a,b) (x,y),(a,b),如果把它拆开, ( x , y ) (x,y) (x,y) ( a , b ) (a,b) (a,b)分别考虑,那么:

  1. 假设转移只需要 ( x , y ) (x,y) (x,y)边, ( a , b ) (a,b) (a,b)边是否存在对转移没有影响,此时概率为 1 2 \frac{1}{2} 21,符合预期。
  2. 转移只需要 ( a , b ) (a,b) (a,b)边同理。
  3. 转移需要两条边,此时两条边都存在的概率为 1 4 \frac{1}{4} 41,比预期少 1 4 \frac{1}{4} 41

因此,可以将第二种情况变成三条边: ( x , y ) (x,y) (x,y),概率为 1 2 \frac{1}{2} 21 ( a , b ) (a,b) (a,b),概率为 1 2 \frac{1}{2} 21 ( x , y , a , b ) (x,y,a,b) (x,y,a,b)四个看作用一条边连接,概率为 1 4 \frac{1}{4} 41

第三种情况同理,将 ( x , y , a , b ) (x,y,a,b) (x,y,a,b)的概率改成 − 1 4 -\frac{1}{4} 41即可。

代码

#include <map>
#include <cstdio>
#include <algorithm>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=15;
const int maxm=15*15*3;
const int maxk=1<<maxn;
const int mod=1000000007;

struct edge
{
  int s,p;

  edge(int _s=0,int _p=0):s(_s),p(_p){}
};

std::map<int,int> f[maxk];
edge e[maxm+10];
int n,m,cnt;

int getf(int sta)
{
  if(sta==0)
    {
      return 1;
    }
  int first=sta&((1<<n)-1),second=(sta>>n)&((1<<n)-1);
  if(f[first].count(second))
    {
      return f[first][second];
    }
  int ans=0;
  for(int i=1; i<=cnt; ++i)
    {
      if(((sta&e[i].s)==e[i].s)&&(sta<(e[i].s<<1)))
        {
          ans=(ans+1ll*getf(sta^e[i].s)*e[i].p)%mod;
        }
    }
  return f[first][second]=ans;
}

int main()
{
  n=read();
  m=read();
  for(int i=1; i<=m; ++i)
    {
      int op=read(),x=read(),y=read(),first=(1<<(x+n-1))|(1<<(y-1));
      e[++cnt]=edge(first,(mod+1)>>1);
      if(op)
        {
          x=read();
          y=read();
          int second=(1<<(x+n-1))|(1<<(y-1));
          e[++cnt]=edge(second,(mod+1)>>1);
          if((first&second)==0)
            {
              if(op==1)
                {
                  e[++cnt]=edge(first|second,(mod+1)>>2);
                }
              else
                {
                  e[++cnt]=edge(first|second,mod-((mod+1)>>2));
                }
            }
        }
    }
  printf("%lld\n",1ll*(1<<n)*getf((1<<(n<<1))-1)%mod);
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值