UOJ#37 主旋律 题解

题♂解♂

方法一

f[S]$f[S]$表示点集（缩过SCC后的一个映射，也就是说这个点集里不存在SCC）是S$S$的情况下的DAG个数。因为一个DAG一定包含一些入度为0$0$的点；同时已经有一个DAG时，如果再往里面加一些入度为0$0$的点，并将这些新加的点向已有的点随意连边（单向的），所得的图还是DAG。我们来枚举这些入度为0$0$的点，但是存在一些情况并不能恰好精准地枚举完（不能保证剩余的点中没有入度为0$0$的点），因此采取容斥。

f[S]=TS,T(1)|T|1×2cross[T][ST]×f[ST]$\displaystyle f[S]=\sum_{T \subset S, T \not = \varnothing} (-1)^{|T|-1} \times 2^{cross[T][S-T]} \times f[S-T]$

方法二

f[S]=TS,T1k|T|(1)k1×gk[T]×2cross[T][ST]×2h[ST]$\displaystyle f[S]=\sum_{T \subset S, T \not = \varnothing} \sum_{1 \leqslant k \leqslant |T|} (-1)^{k-1} \times g_k[T] \times 2^{cross[T][S-T]} \times 2^{h[S-T]}$

f[S]=TS,Tk=1|T|+12g2k1[T]k=1|T|2g2k[T]2cross[T][ST]×2h[ST]$\displaystyle f[S]=\sum_{T \subset S, T \not = \varnothing} \left(\sum_{k=1}^{\lfloor \frac{|T|+1}{2} \rfloor}g_{2k-1}[T]-\sum_{k=1}^{\lfloor \frac{|T|}{2} \rfloor} g_{2k}[T]\right)2^{cross[T][S-T]} \times 2^{h[S-T]}$

p[S]=TS,uTdp[T]×p[ST]+dp[S]$\displaystyle p[S] = -\sum_{T \subset S, u \in T}dp[T] \times p[S - T] + dp[S]$

f[S]=TS,Tp[T]×2cross[T][ST]+h[ST]$\displaystyle f[S]=\sum_{T \subset S, T \not = \varnothing} p[T] \times 2^{cross[T][S-T]+h[S-T]}$

dp[S]=2h[S]f[S]$dp[S] = 2^{h[S]}-f[S]$

代码（C++）

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll pm = 1000000007;
const int N = 15;
int out[N], in[N], fun[1 << N], pw[N * N + 5], cnt[1 << N], cross[1 << N];
ll dp[1 << N], all[1 << N], p[1 << N];
int main() {
int n, m, tn, i, a, b, S, T, rem, t, tmp;
cin >> n >> m; tn = (1 << n) - 1;
for (i = 1; i <= m; ++i) {
cin >> a >> b; --a; --b;
out[a] |= 1 << b; in[b] |= 1 << a;
}
for (i = 0; i < n; ++i) fun[1 << i] = i;
for (pw[0] = i = 1; i <= n * n; ++i) pw[i] = pw[i - 1] * 2 % pm;
for (S = 1; S <= tn; ++S) cnt[S] = cnt[S >> 1] + (S & 1);
dp[0] = all[0] = 1; p[0] = pm - 1;
for (S = 1; S <= tn; ++S) {
if (cnt[S] == 1) {dp[S] = p[S] = all[S] = 1; continue;}
for (i = tmp = 0; i < n; ++i) if (S & (1 << i)) tmp += cnt[S & out[i]];
all[S] = dp[S] = pw[tmp];
rem = S - (S & -S);
for (T = rem; ; T = (T - 1) & rem) {
t = T | (S & -S);
if (t < S) (p[S] += pm - dp[t] * p[S - t] % pm) %= pm;
if (!T) break;
}
for (T = S; T; T = (T - 1) & S) {
if (T == S) cross[T] = 0;
else {
t = fun[(S - T) & (T - S)];
cross[T] = cross[T + (1 << t)] + cnt[in[t] & T] - cnt[out[t] & (S - T - (1 << t))];
}
(dp[S] += pm - p[T] * all[S - T] % pm * pw[cross[T]] % pm) %= pm;
}
(p[S] += dp[S]) %= pm;
}
cout << dp[tn] << endl;
return 0;
}