6554. 【GDOI2020模拟4.11】赢家(winner)
题目描述
PinkRabbit 是一位人赢。
福州市可以抽象成一个n个点m条边的,不包含重边与自环的无向图,PinkRabbit 住在1号
点,而他的妹子住在2号点。
某一天,PinkKitten 施放了一个大魔法,让这个无向图上所有的边都变成了单向边。现在
PinkRabbit 关心的是他是否能够和他的妹子见面。
具体地,PinkRabbit 能和他的妹子见面,当且仅当存在一个点 u,满足新图上从1号点出发能够
到达u,从2号点出发也能到达 。
现在你需要计算出,在把所有 m条边进行定向的所有2^m 种方案中,有多少种方案能让
PinkRabbit 和他的妹子见面。你只需输出其对10^9+7 取模后的结果。
题解
容斥,方案=总方案-不连通
设f[s]表示1/2能且只能到集合s的方案,转移枚举子集S容斥,S内部连通,另一半T方向随便,S-T之间的边全部指向S
算答案类似,枚举1/2能到的S/T,内部连通,指向ST的边单向,其余的任意
注意ST之间不能有连边
写好一些+卡空间可以做到O(3^n),但和O(3^n*n)差不多
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define mod 1000000007
#define ll long long
#define file
using namespace std;
int g[16][32768],h[32768],n,m,i,j,k,l,id,L,s;
ll p[176],f[32768],ans;
bool a[16][16];
void work(int t)
{
f[p[t-1]]=1;
for (i=p[t-1]+4; i<=L; i+=4)
{
f[i]=p[h[i]];
j=i-4;
do {j&=i; f[i]=(f[i]-f[j]*p[h[i^j]])%mod; j-=4;} while (j>0);
}
}
int main()
{
freopen("winner.in","r",stdin);
#ifdef file
freopen("winner.out","w",stdout);
#endif
scanf("%d%d%d",&n,&m,&id);
fo(i,1,m) scanf("%d%d",&j,&k),a[j][k]=a[k][j]=1;
p[0]=1; fo(i,1,n*(n-1)/2) p[i]=p[i-1]*2%mod; L=p[n-1]*2-1;
fo(i,1,n)
{
fo(j,1,L)
{
fo(k,1,n)
if (j&p[k-1])
{g[i][j]=g[i][j^p[k-1]]+a[i][k];break;}
}
}
fo(i,1,L)
{
fo(j,1,n)
if (i&p[j-1])
{h[i]=h[i^p[j-1]]+g[j][i^p[j-1]];break;}
}
work(1);work(2);
ans=p[m];
for (l=3; l<=L; l+=4)
{
i=l-2;
do{
i&=l;j=l^i;
s=m-h[i]-h[j];
fo(k,1,n)
{
if (!(i&p[k-1]) && !(j&p[k-1])) s-=g[k][i]+g[k][j];
if (i&p[k-1] && g[k][j]) break;
}
if (k>n) ans=(ans-f[i]*f[j]%mod*p[s])%mod;
i-=4;
}while (i>0);
}
printf("%lld\n",(ans+mod)%mod);
fclose(stdin);
fclose(stdout);
return 0;
}