P11714 [清华集训 2014] 主旋律 题解
P11714 [清华集训 2014] 主旋律 题解
知识点
状压计数 DP。
题意简述
一个 \(n\) 个点,\(m\) 条边的有向图,现在删除一些边,求删除边后图为强联通的方案数,答案对 \(10^9+7\) 取模。
分析
考虑单步容斥,得到「图为强联通的方案数」就等于「总方案数」减去「图不为强联通的方案数」,所以我们只要求「图不为强联通的方案数」即可。
考虑将图 SCC 缩点,会得到点数大于 \(1\) 的 DAG,所以现在有一种较暴力的做法:枚举每个点所在的 SCC,然后求 「DAG 生成子图方案数」。
「DAG 生成子图方案数」
这类有关 DAG 的计数通常都可以往加入入度为 \(0\) 的点这个方向想。
设 \(f_{S}\) 表示点集为 \(S\) 时的「DAG 生成子图方案数」,可以列一个简单的 DP 转移式:(\(e(T,S)\) 表示「从点集 \(T\) 到 \(S\) 的边数」)
但是这看着就不对……因为在点集 \(S\setminus T\) 中可能存在入度为 \(0\) 的点,这样就算重复了。
- 设 \(F_{T}\) 表示「点集 \(S\) 中入度为 \(0\) 的点组成的集合恰好为 \(T\) 的方案数」。
- 设 \(G_{T}\) 表示「点集 \(S\) 中入度为 \(0\) 的点组成的集合包含点集 \(T\) 的方案数」。
根据定义可以推得:
而我们接下来可以在 \(F_T\) 和 \(G_T\) 之间进行二项式反演:
代入原式化简:
二项式定理推得 \(\sum_{T\subseteq V,T\neq \emptyset} (-1)^{|T|} = -1\)。
这个可以 \(O(3^n)\) 复杂度内计算出来,但是明显这不是正解,于是我们尝试仿照上述过程进行推广,直指我们的本来目的。
「SCC 缩点方案数」
仿照上文,就可以设出以下状态:
- 设 \(f_{S}\) 表示「缩点后点集 \(S\) 为一个 SCC 的方案数」。
- 设 \(g_T\) 表示「缩点后点集 \(T\) 为若干个入度为 \(0\) 的 SCC 的方案数」。
那么我们考虑列式:
最后的这个 \(f_S\) 多出来是因为枚举子集 \(T\) 时,当 \(T=S\),那么会导致 \(g_T\) 中包含 \(f_T\),这部分其实是合法的,不应该减掉。
发现 \(z\) 是不能确定的,因为在我们设状态时,就没有限定 SCC 的数量,所以更改 \(g_T\) 的定义,把容斥系数也放进去,即「缩点后点集 \(T\) 为若干个入度为 \(0\) 的 SCC 的方案中,缩成奇数个的方案数减去缩成偶数个的」。式子变为:
(式中 \(f_S\) 还是多了出来,式子好像变成了恒等式,这个怎么处理?)
接下来求 \(g_T\),这个不难想,只要找子结构即可:
- \(f_S\):表示当只有一个 SCC 时的方案数。
- \(\sum_{T\subsetneq S,\operatorname{lowbit}(S) = \operatorname{lowbit}(T)} f_T g_{S\setminus T}\):增加一个包含 \(\operatorname{lowbit}(S)\) 的 SCC,总数奇偶性改变,符号也改变。
发现此时 \(f_S\) 和 \(g_S\) 互相依赖,而且 \(f_S\) 的式子好像变成了恒等式,我们不知道该怎么求。
但是再细想,发现此时上式中多出来的 \(f_S\) 就有了用武之地:求 \(f_S\) 时,\(g_S\) 转移式中的那个 \(f_S\) 可以先不加,这样 \(f_S\) 转移式中多出来的 \(f_S\) 也可以不加了。所以先算 \(g_S\),但不加 \(f_S\);然后再算 \(f_S\),最后加到 \(g_S\) 中去。
「从点集 \(T\) 到 \(S\) 的边数」
发现最后剩下一个小问题,如何预处理出 \(e(S,T)\)?
考虑总状态数,对于 \(S\),只会用到 \(e(S,S)\) 或者 \(e(T,S\setminus T)(T\subseteq S)\),那么总共只有 \(3^n\) 个,那么预处理可以考虑 vector 开到 \(O(3^n)\) 的空间,也可以对每个 \(S\) 都单独处理。
求出 \(S\) 不变时的递推式,设 \(c(T)=e(T,S\setminus T)\):(\(in_u\) 表示连到 \(u\) 的点集,\(out_u\) 表示 \(u\) 连到的点集)
然后还要单独求出 \(e(S,S)\) 的递推式,我们设 \(d(S) = e(S,S)\):
在编码时,这里的 \(x\) 都取 \(\operatorname{lowbit}\) 即可。
代码
//#define Plus_Cat ""
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define lowbit(x) ((x)&-(x))
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~i;y=(g)[(i=(g)[i].nxt)>0?i:0].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(15+10),M(210+10),St((1<<15)+10);
namespace Modular {
#define Mod 1000000007
int pw[M<<1];
//Modular...
} using namespace Modular;
int n,m,U;
int in[N],out[N],c[St],d[St],f[St],g[St],Lg[St],cnt[St];
signed main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
cin>>n>>m,U=(1<<n)-1;
FOR(i,pw[0]=1,m<<1)pw[i]=mul(pw[i-1],2);
FOR(i,1,m) {
int u,v;
cin>>u>>v,out[u]|=1<<(v-1),in[v]|=1<<(u-1);
}
FOR(i,1,n)Lg[1<<(i-1)]=i;
FOR(S,1,U)cnt[S]=cnt[S>>1]+(S&1);
FOR(S,1,U)d[S]=d[S^lowbit(S)]+cnt[S&in[Lg[lowbit(S)]]]+cnt[S&out[Lg[lowbit(S)]]];
FOR(S,1,U) {
c[S]=0,f[S]=pw[d[S]];
for(int T((S-1)&S); T; T=(T-1)&S) {
int x(lowbit(S^T));
c[T]=c[T|x]-cnt[(S^T)&out[Lg[x]]]+cnt[T&in[Lg[x]]];
}
for(int R(S^lowbit(S)),T((R-1)&R); ~T; T=T?(T-1)&R:-1)toadd(g[S],Mod-mul(f[T|lowbit(S)],g[R^T]));
for(int T(S); T; T=(T-1)&S)toadd(f[S],Mod-mul(g[T],pw[c[T]+d[S^T]]));
toadd(g[S],f[S]);
}
cout<<f[U]<<endl;
return 0;
}

浙公网安备 33010602011771号