题目链接

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

题解

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

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

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

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

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

代码

#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;
}