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
原文地址:https://www.cnblogs.com/Silymtics/p/14514487.html