2023 国庆集训 Day 4A 笔记

· · 个人记录

更佳的阅读体验:2023 国庆集训 Day 4A 笔记,本文同步发表于 洛谷博客。

以下内容为个人总结,如有错误或您有不同见解,欢迎指出。

状压 DP

洛谷 P4363 - 一双木棋

洛谷 P5369 - 最大前缀和

洛谷 P5492 - 随机算法

int ctz[1 << N];
long long f[1 << N], g[1 << N];
long long c[N];
const long long p = 998244353;

cin >> n >> m;

for (int i = 0; i < m; i++) {
    cin >> x >> y;
    c[x] |= 1 << y;
    c[y] |= 1 << x;
}

for (int i = 0; i < n; i++) {
    ctz[1 << i] = i;
}

for (int S = 1; S < 1 << n; S++) {
    int x = ctz[S & -S];
    g[S] = max(g[S - (1 << x)], g[S - (1 << x) - (c[x] & S)] + 1);
}

f[0] = 1;
for (int S = 1; S < 1 << n; S++) {
    for (int x = 0; x < n; x++) {
        if (~S >> x & 1) <=> if (!(S >> x & 1))
            continue;
        if (g[S - (1 << x) - (c[x] & S)] + 1 < g[S])
            continue;
        f[S] = (f[S] + f[S - (1 << x) - (c[x] & S)]) % p;
    }
}

cout << f[(1 << n) - 1] /n! << endl; 

图上拓扑序计数

给定一张 DAG,求其合法拓扑序个数。n \le 20,m \le \frac{n(n - 1)}{2}。

// topo count

long long f[1 << N];

bool valid(int x, int s) {
    // s 没有连向 x 的边
}

for (int s = 1; s < 1 << n; ++s) {
    for (int x = 0; x < n; ++x) {
        if (~s >> x & 1) continue;
        if (valid(x, s)) f[s] += f[s - (1 << x)];
    }
}

边子集拓扑序计数

给定一张有向图(不保证是 DAG)。设其边集为 E 。对于 T \subseteq E 定义 f(T) 为保留 T 时整张图的拓扑序数量,求 ∑_{T \subseteq E}f(T)。n \le 20,m \le n(n-1) 。

// topo count with edge subset

long long count[N][1 << N];

cin >> n >> m;

for (int i = 0; i < m; ++i) {
    cin >> x >> y;
    for (int s = 1; s < 1 << n; ++s)
        if (s >> y & 1) ++count[x][s];
}

for (int s = 1; s < 1 << n; ++s)
    for (int x = 0; x < n; ++x) {
        if (~s >> x & 1) continue;
        f[s] += f[s - (1 << x)] << count[x][s - (1 << x)];
    }

洛谷 P2831 - 愤怒的小鸟

洛谷 P3959 - 宝藏