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

2024-06-10 21:52

本文主要是介绍LibreOJ #10131. 「一本通 4.4 例 2」暗的连锁 题解 树上差分,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

暗的连锁

题目描述

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 1N105,1M2×105。数据保证答案不超过 2 31 − 1 2^{31}−1 2311

原题

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]+1dif[v]+1dif[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) // 此时删去主要边即可破坏整张图,所以任意选择一条次要边都满足条件,贡献为mans += m;if (tree.dif[i] == 1) // 此时删去主要边后必须删去连通两点的唯一次要边,贡献为1ans++;// 当除去直接相连的主要边之外的路径个数大于1时,删去该主要边和任意一条次要边都无法破坏整张图,贡献为0}cout << ans << '\n';return 0;
}

这篇关于LibreOJ #10131. 「一本通 4.4 例 2」暗的连锁 题解 树上差分的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1049369

相关文章

poj 3159 (spfa差分约束最短路) poj 1201

poj 3159: 题意: 每次给出b比a多不多于c个糖果,求n最多比1多多少个糖果。 解析: 差分约束。 这个博客讲差分约束讲的比较好: http://www.cnblogs.com/void/archive/2011/08/26/2153928.html 套个spfa。 代码: #include <iostream>#include <cstdio>#i

uva 10131 最长子序列

题意: 给大象的体重和智商,求体重按从大到小,智商从高到低的最长子序列,并输出路径。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vect

poj 3169 spfa 差分约束

题意: 给n只牛,这些牛有些关系。 ml个关系:fr 与 to 牛间的距离要小于等于 cost。 md个关系:fr 与 to 牛间的距离要大于等于 cost。 隐含关系: d[ i ] <= d[ i + 1 ] 解析: 用以上关系建图,求1-n间最短路即可。 新学了一种建图的方法。。。。。。 代码: #include <iostream>#include

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

POJ 1364差分约束

给出n个变量,m个约束公式 Sa + Sa+1 + .... + Sa+b < ki or > ki ,叫你判断是否存在着解满足这m组约束公式。 Sa + Sa+1   +   .+ Sa+b =  Sum[a+b] - Sum[a-1]  . 注意加入源点n+1 。 public class Main {public static void main(Strin

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

C - Word Ladder题解

C - Word Ladder 题解 解题思路: 先输入两个字符串S 和t 然后在S和T中寻找有多少个字符不同的个数(也就是需要变换多少次) 开始替换时: tips: 字符串下标以0开始 我们定义两个变量a和b,用于记录当前遍历到的字符 首先是判断:如果这时a已经==b了,那么就跳过,不用管; 如果a大于b的话:那么我们就让s中的第i项替换成b,接着就直接输出S就行了。 这样

【秋招笔试】9.07米哈游秋招改编题-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收集 100+ 套笔试题,笔试真题 会在第一时间跟新 🍄 题面描述等均已改编,如果和你笔试题看到的题面描述

LeetCode 第414场周赛个人题解

目录 Q1. 将日期转换为二进制表示 原题链接 思路分析 AC代码 Q2. 范围内整数的最大得分 原题链接 思路分析 AC代码 Q3. 到达数组末尾的最大得分 原题链接 思路分析 AC代码 Q4. 吃掉所有兵需要的最多移动次数 原题链接 思路分析 AC代码 Q1. 将日期转换为二进制表示 原题链接 Q1. 将日期转换为二进制表示 思路分析

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>