题意
响应主旋律的号召,大家决定让这个班级充满爱,现在班级里面有 n 个男生。
如果 a 爱着 b,那么就相当于 a 和 b 之间有一条 a→b 的有向边。如果这 n 个点的图是强联通的,那么就认为这个班级是充满爱的。
不幸的是,有一些不好的事情发生了,现在每一条边都可能被摧毁。我作为爱的使者,想知道有多少种摧毁的方式,使得这个班级任然充满爱呢?(说人话就是有多少边的子集删去之后整个图仍然强联通。)
n≤15,0≤m≤n(n−1),答案对1e9+7取模。
分析
直接求显然不好求,考虑容斥。设
f[S]
表示
S
中的点强连通的方案,
不难注意到如果一个方案不合法,那么它缩点后一定会出现若干个出度为0的点,考虑枚举这些出度为0的点由哪些点组成。
先考虑
枚举
S
中编号最小的点所在的连通块,可以得到
h[S]=∑f[i]∗g[S−i]
如果
g[i]
对
f[S]
作贡献,表明至少有奇数个点出度为0,所以其容斥系数为-1,同理
h[S]
的容斥系数为1,可以得到
f[S]=2S中的边数+∑(h[i]−g[i])∗2(S−i)到i的边数
可以通过预处理一些东西,使得转移的复杂度达到
O(3n)
。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=20;
const int MOD=1000000007;
int n,m,bin[405],map[N],f[40005],g[40005],h[40005],sub[40005],w[40005],cnt[40005];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int lowbit(int x) {return x&(-x);}
void updata(int &x,int y)
{
x+=y;x-=x>=MOD?MOD:0;
}
int main()
{
n=read();m=read();
bin[0]=1;
for (int i=1;i<=max(n,m);i++) bin[i]=bin[i-1]*2,bin[i]-=bin[i]>=MOD?MOD:0;
for (int i=0;i<bin[n];i++) cnt[i]=cnt[i>>1]+(i&1);
for (int i=1;i<=m;i++)
{
int x=read()-1,y=read()-1;
map[x]|=bin[y];
}
for (int s=1;s<bin[n];s++)
{
if (s==lowbit(s)) {f[s]=g[s]=1;continue;}
int top=0;
for (int i=s&(s-1);i;i=s&(i-1)) sub[++top]=i;
for (int i=0;i<n;i++) if (s&bin[i]) w[bin[i]]=cnt[map[i]&s];
for (int i=top;i>=1;i--) w[sub[i]]=w[lowbit(sub[i])]+w[sub[i]^lowbit(sub[i])];
w[s]=w[lowbit(s)]+w[s^lowbit(s)];f[s]=bin[w[s]];
for (int i=1;i<=top;i++)
{
updata(f[s],(LL)(h[sub[i]]+MOD-g[sub[i]])*bin[w[s-sub[i]]]%MOD);
if (!(sub[i]&lowbit(s))) continue;
updata(g[s],(LL)f[sub[i]]*h[s-sub[i]]%MOD);
updata(h[s],(LL)f[sub[i]]*g[s-sub[i]]%MOD);
}
updata(f[s],(h[s]+MOD-g[s])%MOD);
updata(g[s],f[s]);
w[s]=0;
for (int i=1;i<=top;i++) w[sub[i]]=0;
}
printf("%d",f[bin[n]-1]);
return 0;
}