• Loj题解 #10131 暗的连锁


    前置芝士

    • 树上差分

    • 倍增/树剖 求 LCA

    Description

    \(n\) 个结点,有 \(n - 1\) 条边为树边, \(m\) 条边为非树边。减掉一个树边和一个非树边使图不连通,问方案数。
    数据范围: \(n \le 1e5, m \le 2e5\)

    Solution

    考虑一条非树边 \((u, v)\) 的贡献:

    • 会形成一个环
    • 如果选择减掉 \(u \to v\) 上的边,那么非树边只能选这条边才能满足题目要求

    我们称每条附加边 \((u, v)\) 都把树上 \(u, v\) 间的路径覆盖了一次。
    显然,被覆盖了两次的边一定没有可行方案;
    被覆盖了一次的边只能有一种方案;
    没有被覆盖的边可以选任意一条非树边,有 \(m\) 种方案。

    所以用树上差分统计出每条树边被覆盖了几次,最后枚举一遍求每条边的贡献即可

    用树上差分时,假设加一条边 \((u,v)\)
    那么 \(cnt_u ++, cnt_v++, cnt_{lca(u, v)} -= 2\)
    然后 DFS 回溯统计即可
    (自己画个图手模一下)

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 2e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    struct edge{
        int to, nxt;
    }e[MAXN << 1];
    int head[MAXN], num_edge = 0;
    
    int n, m, Ans = 0;
    int cnt[MAXN], ans[MAXN];
    int dep[MAXN], top[MAXN], fath[MAXN], siz[MAXN], son[MAXN];
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
    
    void dfs(int u, int fa){
        fath[u] = fa, dep[u] = dep[fa] + 1, siz[u] = 1;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if(v == fa) continue;
            dfs(v, u);
            siz[u] += siz[v];
            if(siz[son[u]] < siz[v]) son[u] = v;
        }
    }
    
    void dfs2(int u, int tp) {
        top[u] = tp;
        if(son[u]) dfs2(son[u], tp);
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if(v == fath[u] || v == son[u]) continue;
            dfs2(v, v);
        }
    }
    
    int Get_Lca(int u, int v) {
        while(top[u] != top[v]) dep[top[u]] < dep[top[v]] ? v = fath[top[v]] : u = fath[top[u]];
        return dep[u] < dep[v] ? u : v;
    }
    
    void Dfs(int u, int fa) {
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if(v == fa) continue;
            ans[v] = cnt[v];
            Dfs(v, u);
            ans[u] += ans[v];
        }
    }
    
    int main()
    {
        n = read(), m = read();
        for(int i = 1, u, v; i < n; ++i) {
            u = read(), v = read();
            add_edge(u, v), add_edge(v, u);
        }
        dfs(1, 0), dfs2(1, 1);
        for(int i = 1, u, v; i <= m; ++i) {
            u = read(), v = read();
            int lca = Get_Lca(u, v);
            cnt[u] ++, cnt[v] ++, cnt[lca] -= 2;
        }
        Dfs(1, 0);
        for(int i = 1; i <= n; ++i) {
            if(ans[i] == 1) Ans ++;
            if(ans[i] == 0) Ans += m;
        } 
        printf("%d", Ans);
        return 0;
    }
    

    鸣谢

    • 《信息学奥赛一本通—提高篇》
    • 我对不起 Kersen
  • 相关阅读:
    Window 7 + Ubuntu 双系统安装
    Android Proguard
    windows管理方式
    开启Telnet服务
    java中的Date类
    java中String字符串
    java自定义异常和throw、throws的使用
    java异常的嵌套和级联
    java中各种常见的异常
    java异常的基本概念和处理流程
  • 原文地址:https://www.cnblogs.com/Silymtics/p/14514487.html
Copyright © 2020-2023  润新知