P11714 [清华集训 2014] 主旋律
很牛的计数。
正难则反,考虑图不是一个 SCC 的情况,那么缩点后就是一个有 \(> 1\) 个点的 DAG,考虑 DAG 怎么计数,由于每个 DAG 都有若干个入度为 \(0\) 的点,且拿掉这些点之后剩下的还是一个 DAG,这就有子结构了,方便我们 DP。
考虑 \(f_S\) 表示 \(S\) 中的点形成一个 SCC 的方案数,假设 \(E(S, T)\) 表示 \(\sum_{u \in S, v \in T} [(u, v) \in E]\),则 \(f_S = 2^{E(S, S)} - ?\),\(?\) 表示形成若干 SCC 的方案数,那么假设我们拿出了点集 \(T\),形成了 \(k\) 个 \(SCC\),那么容斥系数为 \((-1)^{k + 1}\),设方案数为 \(g_{T, k}\),则 \(f_S = 2^{E(S, S)} - \sum\limits_{T \subset S, T \neq \varnothing} (-1)^{k + 1}g_{T, k} 2^{E(T, T) + E(T, S - T)}\),后面那项是因为这些 SCC 缩点后入度为 \(0\)。
然后考虑把容斥系数放里面,即 \(g_T\) 表示 \(\sum\limits_k (-1)^{k + 1}g_{T, k}\),也就是“形成奇数个 SCC 的方案数”减去“形成偶数个 SCC 的方案数”。
那么考虑转移,\(g_S = f_S - \sum\limits_{T \subseteq S, \text{lowbit}(T) = \text{lowbit}(S)} f_Tg_{S - T}\)
前面一项是因为 \(f_S\) 形成了一个 SCC,后面是因为相较于 \(g_{S - T}\),新增加了一个 SCC 为 \(T\),奇偶反转,所以要减去,另外 \(\text{lowbit}\) 强制要求选,保证了不算重。
然后有一个考虑就是枚举到 \(S\) 的时候先算 \(g_S\),再算 \(f_S\),最后让 \(g_S \leftarrow g_S + f_S\),因为 \(f_S\) 是单独组成一个 SCC,不能减掉,换句话说,这里 \(g_S\) 加上 \(f_S\) 是为了后面的 \(f\) 服务的。
最后考虑 \(E(S, S)\) 和 \(E(T, S - T)\),枚举 \(\text{lowbit}\) 转移即可,后者就对于每个 \(S\) 算一下,没啥好讲的。
时间复杂度 \(\mathcal{O}(3^n)\)。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 1 << 15, M = 15 * 14, mod = 1e9 + 7;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {
cout << arg << ' ';
dbg(args...);
}
namespace Loop1st {
#define lowbit(s) ((s) & -(s))
#define popc __builtin_popcount
int n, m, E1[N], E2[N], in[N], out[N], f[N], g[N], pw[M + 1];
void main() {
cin >> n >> m;
pw[0] = 1;
for (int i = 1; i <= m; i++) pw[i] = (pw[i - 1] << 1) % mod;
for (int i = 0, u, v; i < m; i++) {
cin >> u >> v; u--; v--;
in[1 << v] |= (1 << u); out[1 << u] |= (1 << v);
}
for (int s = 0; s < (1 << n); s++) {
int x = lowbit(s);
E1[s] = E1[s - x] + popc(in[x] & s) + popc(out[x] & s);
}
for (int s = 1; s < (1 << n); s++) {
E2[s] = 0;
for (int t = (s - 1) & s; t; t = (t - 1) & s) {
int x = lowbit(s - t);
E2[t] = E2[t + x] - popc(out[x] & (s - t)) + popc(in[x] & t);
}
for (int t = (s - 1) & s; t; t = (t - 1) & s) if (lowbit(s) == lowbit(t)) {
g[s] = (g[s] - (ll)f[t] * g[s - t] % mod + mod) % mod;
}
f[s] = pw[E1[s]];
for (int t = s; t; t = (t - 1) & s) {
f[s] = (f[s] - (ll)g[t] * pw[E1[s - t] + E2[t]] % mod + mod) % mod;
}
g[s] = (g[s] + f[s]) % mod;
}
cout << f[(1 << n) - 1] << '\n';
}
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T = 1;
// cin >> T;
while (T--) Loop1st::main();
return 0;
}
// start coding at 18:05
// finish debugging at 18:32

浙公网安备 33010602011771号