zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    二进制,八进制,十进制,十六进制之间的转换
    Ajax
    JSP
    事务
    BDUtils
    LG. 1003 铺地毯
    Educational Codeforces Round 25
    POJ.3268 Silver Cow Party (Dijkstra)
    POJ.1797 Heavy Transportation (Dijkstra变形)
    POJ. 2253 Frogger (Dijkstra )
  • 原文地址:https://www.cnblogs.com/quanjun/p/15705203.html
Copyright © 2011-2022 走看看