隐藏
「NOIP十连赛day4」巨神兵 - 容斥原理 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「NOIP十连赛day4」巨神兵 - 容斥原理

题目大意

    求一个有向图中不含有一个强连通分量的子图数目。


题目分析

主旋律弱化版。
用$f[S]$表示$S$集合中不含环的子图数目。
枚举$S$的子集$T$,使得$T$作为DAG的汇点不连出任何边,$S-T$可以向$T$随意连边。
在转移的时候顺便统计跨越集合的边的数目。
这样转移可能会重,按照$T$集合大小容斥一下即可。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;

typedef long long LL;

inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}

#define lowbit(x) ((x)&(-(x)))

const int maxs=(1<<17)+5;
const LL mod=1e9+7;

int n,m;
LL Pow[maxs],In[maxs],Out[maxs],bitcnt[maxs],f[maxs],p[maxs];

int main() {
n=Get_Int();
m=Get_Int();
for(int i=1; i<=m; i++) {
int x=1<<(Get_Int()-1),y=1<<(Get_Int()-1);
Out[x]|=y;
In[y]|=x;
}
Pow[0]=1;
for(int i=1; i<=n*n; i++)Pow[i]=(Pow[i-1]<<1)%mod;
bitcnt[0]=0;
for(int i=1; i<(1<<n); i++)bitcnt[i]=bitcnt[i^lowbit(i)]+1;
f[0]=1;
for(int S=1; S<(1<<n); S++)
for(int T=S; T; T=(T-1)&S) {
if(T!=S) {
int u=lowbit(S^T);
p[T]=p[T^u]+bitcnt[Out[u]&T]-bitcnt[In[u]&(S^T)];
} else p[T]=0;
if(bitcnt[T]&1)f[S]=(f[S]+f[S^T]*Pow[p[T]]%mod)%mod;
else f[S]=(f[S]-f[S^T]*Pow[p[T]]%mod+mod)%mod;
}
printf("%lld\n",f[(1<<n)-1]);
return 0;
}
姥爷们赏瓶冰阔落吧~