3812: 主旋律
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 332 Solved: 269
[Submit][Status][Discuss]
Description
响应主旋律的号召,大家决定让这个班级充满爱,现在班级里面有 n 个男生。
如果 a 爱着 b,那么就相当于 a 和 b 之间有一条 a→b 的有向边。如果这 n 个点的图是强联通的,那么就认为这个班级是充满爱的。
不幸的是,有一些不好的事情发生了,现在每一条边都可能被摧毁。我作为爱的使者,想知道有多少种摧毁的方式,使得这个班级任然充满爱呢?(说人话就是有多少边的子集删去之后整个图仍然强联通。)
Input
第一行两个数 n 和 m,表示班级里的男生数和爱的关系数。
接下来 m 行,每行两个数 a 和 b,表示男生 a 爱着男生 b。同时 a 不等于 b。
所有男生从 1 到 n 标号。
同一条边不会出现两遍,但可能出现 a 爱着 b,b 也爱着 a 的情况,这是两条不同的边。
Output
输出一行一个整数,表示对 109+7 取模后的答案。
Sample Input
5 15
4 3
4 2
2 5
2 1
1 2
5 1
3 2
4 1
1 4
5 4
3 4
5 3
2 3
1 5
3 1
4 3
4 2
2 5
2 1
1 2
5 1
3 2
4 1
1 4
5 4
3 4
5 3
2 3
1 5
3 1
Sample Output
9390
HINT
对于 100% 的数据满足: n≤15,0≤m≤n(n−1)。
这是一道好题
运用正难则反的思想,考虑补集转化,求整个图不强连通的方案数
那么若整个图不强连通,则缩点之后一定为一个拓扑图的集合
枚举没有出度的scc的集合 由其他点与之相连 则会构成DAG
这时考虑固定的集合若有奇数个scc,则容斥系数为-1,偶数为1
令 f[i] 表示 i 状态下合法方案数,g[i] 表示 i 状态下奇数个scc的方案数,h[i] 表示偶数个scc的方案数
则可以得到转移
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=10*x+ch-'0';ch=getchar();}
return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}
const int N=16,mod=int(1e9)+7;
int n,m;
int f[(1<<N)+100],g[(1<<N)+100],h[(1<<N)+100];
// f the whole graph is a scc
// g the number of scc is odd
// h the number of scc is even
int pw[N*N],bit[(1<<N)+100],w[(1<<N)+100][2];
int in[(1<<N)+100],out[(1<<N)+100];
int main()
{
n=read(),m=read();
register int i,j,u,v,tmp;
for(i=1;i<=m;++i)
u=read(),v=read(),
out[1<<(u-1)]|=1<<(v-1),in[1<<(v-1)]|=1<<(u-1);
for(i=1;i<(1<<n);++i) bit[i]=bit[i>>1]+(i&1);
pw[0]=1;for(i=1;i<=m;++i) pw[i]=(pw[i-1]<<1)%mod;
h[0]=1;
for(i=1;i<(1<<n);++i)
{
for(j=i;j;j-=(j&-j))
w[i][0]+=bit[out[j&-j]&i];
if(i==(i&-i))
{f[i]=g[i]=1;continue;}
f[i]=pw[w[i][0]];
for(j=i&(i-1);j;j=i&(j-1))
if(j&(i&-i))
(g[i]+=1ll*f[j]*h[i^j]%mod)%=mod,
(h[i]+=1ll*f[j]*g[i^j]%mod)%=mod;
for(j=i&(i-1);j;j=i&(j-1))
{
tmp=(i^j)&-(i^j),
w[j][1]=w[j^tmp][1]-bit[in[tmp]&(i^j^tmp)]+bit[out[tmp]&(j)];
(f[i]+=1ll*(h[j]-g[j])*pw[w[i^j][0]+w[j][1]]%mod)%=mod;
}
(f[i]+=(h[i]-g[i])%mod)%=mod;
if(f[i]<0) f[i]+=mod;
(g[i]+=f[i])%=mod;
}
cout<<f[(1<<n)-1]<<endl;
return 0;
}