2020暑期牛客多校训练营第十场(C)Decrement on the Tree(图论,set)
程序员文章站
2022-06-02 22:41:44
...
Decrement on the Tree
题目描述:
你得到一棵树。 有n个顶点和n-1个边。 树中的每个边都有一个非负的权重。 每次都可以选择两个不同的顶点u,v,并将路径上每个边的权重减去1。要使所有边的权重变为零。
最小操作数是多少?
您还需要支持边权重的修改:将第p个边的权重更改为w。 每次修改后,您都需要输出答案。
输入描述:
第一行包含两个整数
在接下来的行中,每行包含三个整数------权重为的边 。 边缘按顺序从索引到。
在接下来的行中,每行包含两个整数。
输出描述:
输出个整数,原始树的答案以及每次修改后的答案。
样例输入:
5 3
1 2 3
1 3 4
2 4 5
3 5 6
1 10
2 10
3 10
样例输出:
8
12
10
10
思路:
观察题意,我们发现有几次操作就是覆盖几条线段(路径),所以我们只要关注路径的起点和终点,路径数就是起点终点的数量和。
如果有一条边的权值很大,超过了其他所有边的权值和,那么必然会出现某条边非常多的情况,起点和终点的数量为这条边的数量*2,就像最后一个图形一样。
如果没有超过,那么就会有前两种情况出现。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 1e5 + 10;
multiset < ll > s[MAXN];
multiset < ll > :: iterator it;
int n, q, a[MAXN], b[MAXN];
ll w[MAXN], sum[MAXN], ans;
ll date (int x) {
it = s[x].end();
--it;
ll maxn = *it;
if (maxn * 2 > sum[x]) return 2 * maxn - sum[x];
return sum[x] & 1;
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1 ; i < n ; ++i){
scanf("%d%d%lld", a+i, b+i, w+i);
s[a[i]].insert(w[i]);
s[b[i]].insert(w[i]);
sum[a[i]] += w[i];
sum[b[i]] += w[i];
}
for(int i = 1 ; i <= n ; ++i)
ans += date(i);
printf("%lld\n", ans/2ll);
while (q--) {
int p;
ll val;
scanf("%d%lld", &p, &val);
ans -= date(a[p]) + date(b[p]);
s[a[p]].erase(s[a[p]].find(w[p]));
s[b[p]].erase(s[b[p]].find(w[p]));
s[a[p]].insert(val);
s[b[p]].insert(val);
sum[a[p]] += val - w[p];
sum[b[p]] += val - w[p];
w[p] = val;
ans += date(a[p]) + date(b[p]);
printf("%lld\n", ans/2ll);
}
}
推荐阅读
-
2020牛客暑期多校训练营(第二场)C Cover the Tree
-
2020牛客暑期多校第二场 C - Cover the Tree(思维/贪心)
-
C.Decrement on the Tree .2020牛客暑期多校训练营(第十场)
-
2020牛客多校第十场C-Decrement on the Tree
-
2020暑期牛客多校训练营第十场(C)Decrement on the Tree(图论,set)
-
2020牛客暑期多校训练营Decrement on the Tree(图论,set)
-
2020牛客暑期多校训练营(第二场)C Cover the Tree
-
2020牛客暑期多校第二场 C - Cover the Tree(思维/贪心)