UOJ #37. [清华集训 2014] 主旋律

UOJ 传送门

考虑 dp。设 fS 为点集 S 构成强连通分量的方案数。

容易想到容斥。设 edSS 内部连边数,那么 fS 就是总的方案数 2edS 减去构成的不是强连通分量的方案数。

我们考虑如果整个图不是一个强连通分量,那么缩点后一定有 >1 个分量,并且缩完点后图变成一个 DAG。

由此我们想到钦定 TS 为点集 T 是入度为 0 的所有强连通分量点集的并集(注意这里 T 可以 =S,因为可以有 >1 个入度为 0 的强连通分量)。

但是这样会有算重,因为我们无法保证 T 中的分量就一定是全部入度为 0 的强连通分量。考虑容斥,容斥系数为 (1)cnt1,其中 cnt 为强连通分量个数,表示选奇数个强连通分量的系数为 +1,偶数个为 1

gTT 中分成奇数个分量的方案数减去分成偶数个分量的方案数。容易转移:gT=wTfwgTw。注意这里我们钦定 w 包含 T 的最低位。

那么 fS 就能转移了。设 hT 为边 uv 的数量,其中 uS,vT,那么这个东西可以递推求出。我们要限制其他点不能向 T 中的点连边,那么 fS=2edSTS2edShTgT

code
// Problem: #37. 【清华集训2014】主旋律
// Contest: UOJ
// URL: https://uoj.ac/problem/37
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define lowbit(x) ((x) & (-(x)))
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 220;
const int maxm = (1 << 15) + 50;
const ll mod = 1000000007;
ll n, m, in[maxn], pw[maxn], f[maxm], g[maxm], h[maxm], ed[maxm], lg[maxm];
int stk[maxm], top;
pii E[maxn];
void solve() {
scanf("%lld%lld", &n, &m);
for (int i = 0; i < n; ++i) {
lg[1 << i] = i;
}
pw[0] = 1;
for (int i = 1; i < maxn; ++i) {
pw[i] = pw[i - 1] * 2 % mod;
}
for (int i = 1, u, v; i <= m; ++i) {
scanf("%d%d", &u, &v);
--u;
--v;
in[v] |= (1 << u);
E[i] = make_pair(u, v);
}
for (int S = 1; S < (1 << n); ++S) {
for (int i = 1; i <= m; ++i) {
int u = E[i].fst, v = E[i].scd;
if ((S & (1 << u)) && (S & (1 << v))) {
++ed[S];
}
}
}
for (int S = 1; S < (1 << n); ++S) {
f[S] = pw[ed[S]];
for (int T = S; T; T = (T - 1) & S) {
stk[++top] = T;
}
while (top) {
int T = stk[top--];
h[T] = h[T ^ lowbit(T)] + __builtin_popcount(in[lg[lowbit(T)]] & S);
}
for (int T = (S - 1) & S; T; T = (T - 1) & S) {
if (T & lowbit(S)) {
g[S] = (g[S] - f[T] * g[S ^ T] % mod + mod) % mod;
}
}
for (int T = S; T; T = (T - 1) & S) {
f[S] = (f[S] - pw[ed[S] - h[T]] * g[T] % mod + mod) % mod;
}
g[S] = (g[S] + f[S]) % mod;
}
printf("%lld\n", f[(1 << n) - 1]);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
posted @   zltzlt  阅读(481)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示