loj10131. 「一本通 4.4 例 2」暗的连锁 题解 树上差分(边差分)

题目链接:https://loj.ac/p/10131

题目大意:

Dark 是一张无向图,图中有 \(N\) 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark 有 \(N-1\) 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark 还有 \(M\) 条附加边。

你的任务是把 Dark 斩为不连通的两部分。一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断 Dark 的一条附加边。

现在你想要知道,一共有多少种方案可以击败 Dark。注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。

解题思路:

根据题意,“主要边”构成一棵树,“附加边”则是“非树边”。把一条附加边 \((x,y)\) 添加到主要边构成的树中,会与树上 \(x,y\) 之间的路径一起形成一个环。如果第一步选择切断 \(x,y\) 之间路径上的某条边,那么第二步就必须切断附加边 \((x,y)\) ,才能使 Dark 被斩为不连通的两部分。

因此,我们称每条附加边 \((x,y)\) 都把树上 \(x,y\) 之间的路径上的每条边“覆盖了一次”。我们只需统计出每条“主要边”被覆盖了多少次。若第一步把覆盖 \(0\) 次的主要边切断,则第二步可以切断任意一条附加边。若第一步把覆盖 \(1\) 次的主要边切断,则第 \(2\) 步方法唯一。若第一步把覆盖 \(2\) 次及以上的主要边切断,则第二步无论如何操作都不能击败 Dark。这样我们就得到了击败 Dark 的方案数。

使用树上差分(边差分),即对于任意 \((x,y)\),设它们的 LCA 为 \(p\),差分数组为 \(d\),则 \(d[u]\)\(d[v]\) 依次加 \(1\),而 \(d[p]\)\(2\)

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, m, pa[maxn][21], dep[maxn];
vector<int> g[maxn];
int d[maxn], ans;
void dfs(int u, int p) {
    dep[u] = dep[p] + 1;
    pa[u][0] = p;
    for (int i = 1; (1<<i) <= dep[u]; i ++)
        pa[u][i] = pa[ pa[u][i-1] ][i-1];
    for (auto v : g[u])
        if (v != p)
            dfs(v, u);
}
int lca(int x, int y) {
    if (dep[x] < dep[y]) swap(x, y);
    for (int i = 20; i >= 0; i --) {
        if (dep[ pa[x][i] ] >= dep[y]) x = pa[x][i];
        if (x == y) return x;
    }
    for (int i = 20; i >= 0; i --) {
        if (pa[x][i] != pa[y][i]) {
            x = pa[x][i];
            y = pa[y][i];
        }
    }
    return pa[x][0];
}
void dfs2(int u, int p) {
    for (auto v : g[u])
        if (v != p)
           dfs2(v, u), d[u] += d[v];
}
int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    for (int i = 1; i < n; i ++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, 0);
    for (int i = 0; i < m; i ++) {
        int u, v, p;
        cin >> u >> v;
        p = lca(u, v);
        d[u] ++;
        d[v] ++;
        d[p] -= 2;
    }
    dfs2(1, 0);
    for (int i = 2; i <= n; i ++) {
        if (d[i] == 0) ans += m;
        else if (d[i] == 1)ans ++;
    }
    cout << ans << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/quanjun/p/15705203.html