首页 > 其他分享 >LibreOJ #10131. 「一本通 4.4 例 2」暗的连锁 题解 树上差分

LibreOJ #10131. 「一本通 4.4 例 2」暗的连锁 题解 树上差分

时间:2024-06-10 19:29:20浏览次数:19  
标签:4.4 10131 int 题解 dif tree Dark fa 主要

暗的连锁

题目描述

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

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

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

输入描述

第一行包含两个整数 N 和 M;

之后 N - 1 行,每行包括两个整数 A 和 B,表示 A 和 B 之间有一条主要边;

之后 M 行以同样的格式给出附加边。

输出描述

输出一个整数表示答案。

样例 #1

样例输入 #1

4 1
1 2
2 3
1 4
3 4

样例输出 #1

3

提示

对于 100% 的数据, 1 ≤ N ≤ 1 0 5 , 1 ≤ M ≤ 2 × 1 0 5 1≤N≤10^5,1≤M≤2×10^5 1≤N≤105,1≤M≤2×105。数据保证答案不超过 2 31 − 1 2^{31}−1 231−1。

原题

LOJ——传送门

思路

题目求的是切断一条主要边和一条附加边后使得整张图变为不连通的两部分的方案数。由于题目保证所有主要边构成一棵树,我们可以枚举所有主要边,然后判断有多少个附加边与该主要边组合后能使得整张图变为不连通的两部分。事实上,当我们枚举每个点到其父亲的主要边时,一共有三种情况:

  • 当除去直接相连的主要边之外的连通两点的路径个数 = 0 =0 =0 时,删去该主要边即可破坏整张图,任意选择一条次要边都满足条件,贡献为 m m m。
  • 当除去直接相连的主要边之外的连通两点的路径个数 = 1 =1 =1 时,删去该主要边后必须删去连通两点的唯一次要边,贡献为 1 1 1。
  • 当除去直接相连的主要边之外的连通两点的路径个数 > 1 >1 >1 时,删去该主要边和任意一条次要边都无法破坏整张图,贡献为 0 0 0。

问题就只剩下如何求某个点到其父亲的路径个数。我们可以采用树上差分的方法,对于任意一条连接节点 u 和节点 v 的附加边,我们令 d i f [ u ] + 1 , d i f [ v ] + 1 , d i f [ L C A ( u , v ) ] − 2 dif[u]+1,dif[v]+1,dif[LCA(u,v)]-2 dif[u]+1,dif[v]+1,dif[LCA(u,v)]−2。然后再对树上差分求前缀和,这样所得到的 d i f [ i ] dif[i] dif[i] 就记录了节点 i i i 与它父亲之间的路径个数(除去直接相连的主要边之外)。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
typedef long long ll;

const int MAX = 2e5 + 6;
int n, m;
struct TREE
{
    vector<int> e[MAX];
    int depth[MAX];
    int fa[MAX][26];
    int dif[MAX];
    void add(int u, int v) // 添加无向边
    {
        e[u].push_back(v);
        e[v].push_back(u);
    }
    void getDepth(int u, int fath) // 获得每个节点的深度及父亲
    {
        fa[u][0] = fath;
        depth[u] = depth[fath] + 1;
        for (auto v : e[u])
        {
            if (v != fath)
                getDepth(v, u);
        }
    }
    void init()
    {
        getDepth(1, 1);
        for (int i = 1; i <= 20; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                fa[j][i] = fa[fa[j][i - 1]][i - 1]; // 记录每个节点的倍增祖先
            }
        }
    }
    int lca(int x, int y)
    {
        if (depth[x] < depth[y])
            swap(x, y);
        // 先将x和y的深度调节成一致的
        for (int i = 20; i >= 0; i--)
        {
            if (depth[fa[x][i]] >= depth[y])
            {
                x = fa[x][i];
            }
        }
        // 再一起向低深度跳跃,从而找到最近公共祖先
        if (x == y)
            return x;
        for (int i = 20; i >= 0; i--)
        {
            if (fa[x][i] != fa[y][i])
            {
                x = fa[x][i];
                y = fa[y][i];
            }
        }
        return fa[x][0];
    }
    void insert(int u, int v) // 树上差分
    {
        dif[u]++;
        dif[v]++;
        dif[lca(u, v)] -= 2;
    }
    void dfs(int u) // 用dfs求出树上差分的前缀和
    {
        for (auto v : e[u])
        {
            if (v != fa[u][0])
            {
                dfs(v);
                dif[u] += dif[v];
            }
        }
    }
} tree;

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> n >> m;
    int u, v;
    for (int i = 1; i < n; i++)
    {
        cin >> u >> v;
        tree.add(u, v); // 添加主要边
    }
    tree.init();
    for (int i = 1; i <= m; i++)
    {
        cin >> u >> v;
        tree.insert(u, v); // 添加次要边
    }
    tree.dfs(1); // 树上差分求前缀和,从而求出每个节点到其父亲节点(除去直接相连的主要边之外的)的路径个数
    i64 ans = 0;
    for (int i = 2; i <= n; i++)
    {
        if (tree.dif[i] == 0) // 此时删去主要边即可破坏整张图,所以任意选择一条次要边都满足条件,贡献为m
            ans += m;
        if (tree.dif[i] == 1) // 此时删去主要边后必须删去连通两点的唯一次要边,贡献为1
            ans++;
        // 当除去直接相连的主要边之外的路径个数大于1时,删去该主要边和任意一条次要边都无法破坏整张图,贡献为0
    }
    cout << ans << '\n';

    return 0;
}

标签:4.4,10131,int,题解,dif,tree,Dark,fa,主要
From: https://blog.csdn.net/bbc_plus/article/details/139578684

相关文章

  • [题解]P9433 [NAPC-#1] Stage5 - Conveyors
    P9433[NAPC-#1]Stage5-Conveyors题意简述给定一个\(N\)个节点的树形结构,每条边有边权,树上有\(k\)个关键点。接下来有\(q\)次询问,每次询问给定\(x,y\)两点,请计算从\(x\)开始经过这\(k\)个关键点(可以重复经过)再到\(y\)的最短路程。解题思路我们可以发现,如果\(x\)与\(y\)都......
  • 牛客周赛 Round 46 题解 C++
    目录 A 乐奈吃冰B 素世喝茶C 爱音开灯D 小灯做题E 立希喂猫F 祥子拆团 A 乐奈吃冰#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<vector>#include<unordered_map>......
  • 斜率优化DP简单总结&&“土地购买”题解
    今天刚刷完了斜率优化DP,简单从头回顾一下。\[首先,能写出DP方程应该是最重要的,毕竟斜率只是用来优化的\]那么一个DP方程能用斜率优化,具备一种形式:\[f[i]+s1[i]+A[i]*B[j]=f[j]+s2[j]\]其中,f[i]表示所求值,(s1[i]、A[i])与(s2[j]、B[j])分别表示只与i或j有关的一个表达式(可以是只有常......
  • Codeforces Round 837题解(A、B)
    A.HossamandCombinatorics\(|a_i-a_j|\)最大的就是最大值和最小值,注意要开longlong。intn;inta[N];voidsolve(){cin>>n;intmin_v=INF,max_v=0;for(inti=1;i<=n;i++){cin>>a[i];min_v=min(min_v,a[i......
  • CF1970F1 Playing Quidditch (Easy) 题解
    一道大模拟题。这道题可以用一个 map 记录球员及鬼飞球当时的坐标,用一个数组 a 记录是否有人进球,用另一个数组 b 记录每位球员是否有鬼飞球。当球员抓住鬼飞球后,鬼飞球跟着这个球员移动,直到这个球员投球。话不多说,直接上代码。MyCode:#include<bits/stdc++.h>#de......
  • 【题解】 [CSP-J 2019] 纪念品
    题目描述题目大意在\(T\)天内,有\(n\)种纪念品和初始的\(m\)元。可以得到每天每种纪念品的价格。每一天可以以当日价格买卖纪念品。特别的,当天卖出得到的钱可以当天买入,当日买入的纪念品也可以当日卖出。当然可以一直持有。但是,\(T\)天过后,手上不可以持有纪念品。思路......
  • 【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 机场航班调度程序(100分) - 三语言A
    ......
  • 【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 最富裕的小家庭(100分) - 三语言AC
    ......
  • P7690 [CEOI2002] A decorative fence 题解
    cnblogs如果只是询问方案数,是P2467[SDOI2010]地精部落这道题,设\(f_{i,j,1/0}\)表示以\(j\)个数中从小到大的第\(i\)个数处于高/低位开头的方案数。显然有\[\begin{aligned}\begin{cases}f_{i,j,1}=\sum_{k=1}^{i-1}f_{k,j-1,0}\\f_{i,j,0}=\sum_{k=i}......
  • C++题解——3320——竞选总统(信息学奥赛一本通)
    题目描述:小明想当Y国的总统,Y国大选是按各州的投票结果来确定最终的结果的,如果得到超过一半的州的支持就可以当选,而每个州的投票结果又是由该州选民投票产生的,如果某个州超过一半的选民支持小明,则他将赢得该州的支持。现在给出每个州的选民人数,请问小明至少需要赢得多少选民的......