题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=5006
题解
首先假设只有第一种情况(t=0)(t=0)(t=0),记f[i][j]f[i][j]f[i][j]表示左边的匹配状态为iii,右边匹配状态为jjj时,匹配方案的期望。转移枚举左边的最高位匹配到了哪条边,这样可以避免重复转移。
考虑第二种情况(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)分别考虑,那么:
- 假设转移只需要(x,y)(x,y)(x,y)边,(a,b)(a,b)(a,b)边是否存在对转移没有影响,此时概率为12\frac{1}{2}21,符合预期。
- 转移只需要(a,b)(a,b)(a,b)边同理。
- 转移需要两条边,此时两条边都存在的概率为14\frac{1}{4}41,比预期少14\frac{1}{4}41。
因此,可以将第二种情况变成三条边:(x,y)(x,y)(x,y),概率为12\frac{1}{2}21;(a,b)(a,b)(a,b),概率为12\frac{1}{2}21;(x,y,a,b)(x,y,a,b)(x,y,a,b)四个看作用一条边连接,概率为14\frac{1}{4}41。
第三种情况同理,将(x,y,a,b)(x,y,a,b)(x,y,a,b)的概率改成−14-\frac{1}{4}−41即可。
代码
#include
#include
#include
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;
}