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
",(ans+mod)%mod);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/12691717.html