正题
luogu
AT3634
题目大意
给你一个图,让你给图上的边定方向,问1,2两个点可以到同一个点的方案数
解题思路
直接求可以到同一个点不好求,可以用总方案数减去不合法方案数,即到不了同一个点的方案数
那么就是有若干点把1,2分开,设 f1/2,if_{1/2,i}f1/2,i 表示从 1/2 开始走,可以走到状态 i 中的点,这一部分直接求也不好求,考虑容斥
先计算出 i 中的边随意定向的方案数(记为s),然后枚举一个状态 j,表示只能到 j 的方案数,对于补集中的边随便选(因为无法到补集中的任何一个点,所以里面的边的方向不影响),那么有
f1/2,i=2si−∑f1/2,j×2si⊕jf_{1/2,i}=2^{s_i}-\sum f_{1/2,j}\times 2^{s_{i\oplus j}}f1/2,i=2si−∑f1/2,j×2si⊕j
得到 f 后枚举1,2个点能到的点即可
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 400
#define M 100000
#define mod 1000000007
using namespace std;
ll n,m,k,ans,X[N],Y[N],pw[N],s[M],f[3][M];
int main()
{scanf("%lld%lld",&n,&m);k=(1<<n)-1;for(int i=1;i<=m;++i)scanf("%lld%lld",&X[i],&Y[i]);pw[0]=1;for(int i=1;i<=m;++i)pw[i]=pw[i-1]*2%mod;for(int i=0;i<=k;++i)for(int j=1;j<=m;++j)if((i&(1<<X[j]-1))&&(i&(1<<Y[j]-1)))s[i]++;for(int g=1;g<=2;++g)for(int i=0;i<=k;++i){if(!(i&(1<<g-1)))continue;f[g][i]=pw[s[i]];for(int j=i;j;j=(j-1)&i)f[g][i]=(f[g][i]-f[g][i^j]*pw[s[j]]%mod+mod)%mod;}ans=pw[m];for(int i=0;i<=k;++i)for(int j=k^i;j;j=(j-1)&(k^i))//点不能有交集if(s[i]+s[j]==s[i|j])//不存在边直接相连ans=(ans-f[1][i]*f[2][j]%mod*pw[s[k^(i|j)]]%mod+mod)%mod;printf("%lld",ans);return 0;
}