Description
响应主旋律的号召,大家决定让这个班级充满爱,现在班级里面有 n 个男生。
如果 a 爱着 b,那么就相当于 a 和 b 之间有一条 a→b 的有向边。如果这 n 个点的图是强联通的,那么就认为这个班级是充满爱的。
不幸的是,有一些不好的事情发生了,现在每一条边都可能被摧毁。我作为爱的使者,想知道有多少种摧毁的方式,使得这个班级任然充满爱呢?(说人话就是有多少边的子集删去之后整个图仍然强联通。)
n
≤
15
n\le15
n≤15
Solution
题意无力吐槽。。
容易想到设f[S]表示S这个点集合选出来组成强连通图的方案数,我们可以容斥减去不合法的方案
具体说就是减去至少奇数个强连通分量的方案数加上至少偶数个强连通分量的方案数,那么设g[S]表示S组成奇数个强连通分量的方案数,h[S]表示偶数个,那么转移有
g
[
S
]
=
∑
T
⊂
S
h
[
T
]
×
f
[
S
−
T
]
g[S]=\sum_{T\subset S}{h[T]\times f[S-T]}
g[S]=∑T⊂Sh[T]×f[S−T],偶数的话同理
于是直接做就可以做到 O ( n 3 n ) O(n3^n) O(n3n)了,预处理一下就能 O ( 3 n ) O(3^n) O(3n)
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define lowbit(x) (x&-x)
typedef long long LL;
const int MOD=1e9+7;
const int N=200005;
LL f[N],g[N],h[N],s[N],c[N],r[21],w[N],st[N],bin[N];
int main(void) {
bin[0]=1; rep(i,1,N) bin[i]=bin[i-1]*2%MOD;
freopen("data.in","r",stdin);
int n,m; scanf("%d%d",&n,&m);
rep(i,1,m) {
int x,y; scanf("%d%d",&x,&y);
r[x-1]|=(1<<y-1);
}
for (int i=1;i<(1<<n);++i) c[i]=(c[i>>1])+(i&1);
for (int S=1;S<(1<<n);++S) {
if (c[S]==1) {f[S]=g[S]=1; continue;}
int top=0;
for (int T=(S-1)&S;T;T=(T-1)&S) st[++top]=T;
rep(i,0,n-1) if ((S>>i)&1) w[1<<i]=c[r[i]&S];
drp(i,top,1) w[st[i]]=w[st[i]-lowbit(st[i])]+w[lowbit(st[i])];
w[S]=w[S-lowbit(S)]+w[lowbit(S)]; f[S]=bin[w[S]];
rep(i,1,top) {
int T=st[i];
f[S]=(f[S]+(h[T]-g[T]+MOD)*bin[w[S-T]]%MOD)%MOD;
if (lowbit(S)!=lowbit(T)) continue;
g[S]=(g[S]+f[T]*h[S-T]%MOD)%MOD;
h[S]=(h[S]+f[T]*g[S-T]%MOD)%MOD;
}
f[S]=(f[S]+h[S]-g[S]+MOD)%MOD;
g[S]=(g[S]+f[S])%MOD;
rep(i,1,top) w[st[i]]=0; w[S]=0;
}
printf("%lld\n", f[(1<<n)-1]);
return 0;
}