P11714 题解
Linge_Zzzz · · 题解
其实是整合了多篇题解的长处,这有人看不懂我直接吃。
Sol
发现强连通很难刻画,不妨使用单步容斥,转化成算非强连通方案数。
考虑刻画一下非强连通,发现缩点之后一定是一个点数大于
于是,呃,考虑一种比较劣的做法,就是先暴力枚举这个图缩点之后每个点属于的 SCC 是哪个,然后对着这个算方案数。
于是现在需要算 DAG 生成子图。
考虑刻画 DAG,发现其一定存在一些点入度为
设
但是这对吗?这不对。因为
考虑一下子集反演,当前全集为
反演得到:
重写一下转移式,进行一些式子的推导:
于是得到了一个
考虑拓展这个东西到非强连通图计数上。
设
但是发现容斥系数是不确定的,因为并不知道
这个式子可以
考虑找子结构,发现这些缩完之后的点互不相干,于是可以钦定
第一项是考虑
但是又出现了新问题,
所以先算出
于是现在只剩下最后一个问题了,怎么算
看起来这个状态数是
先预处理出
这样,整个问题就能做到
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int> pii;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
const int N=18,INF=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
int n,m,dp[1<<N],g[1<<N],in[N],out[N],e1[1<<N],e2[1<<N],p2[1145],pos[1<<N];
inline int lowbit(int x){return x&(-x);}
inline int popcnt(int x){return __builtin_popcount(x);}
vector<int> G[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
p2[0]=1;
for(int i=1;i<=1000;i++)p2[i]=p2[i-1]*2%mod;
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
out[u]|=(1<<(v-1));
in[v]|=(1<<(u-1));
}
for(int i=0;i<n;i++)pos[1<<i]=i+1;
for(int S=1;S<(1<<n);S++){
int x=lowbit(S);
e2[S]=e2[S-x]+popcnt(in[pos[x]]&S)+popcnt(out[pos[x]]&S);
}
for(int S=1;S<(1<<n);S++){
e1[S]=0;
for(int T=(S-1)&S;T;T=(T-1)&S){
int x=lowbit(S-T);
e1[T]=e1[T+x]-popcnt((S-T)&out[pos[x]])+popcnt(T&in[pos[x]]);
}
for(int T=(S-1)&S;T;T=(T-1)&S){
if(lowbit(S)!=lowbit(T))continue;
g[S]=(g[S]-dp[T]*g[S-T]%mod+mod)%mod;
}
dp[S]=p2[e2[S]];
for(int T=S;T;T=(T-1)&S){
dp[S]=(dp[S]-g[T]*p2[e1[T]+e2[S-T]]%mod+mod)%mod;
}
g[S]=(g[S]+dp[S])%mod;
}
cout<<dp[(1<<n)-1]<<endl;
return 0;
}