2020暑期牛客多校训练营第十场(C)Decrement on the Tree(图论,set)

Decrement on the Tree

原题请看这里

题目描述:

你得到一棵树。 有n个顶点和n-1个边。 树中的每个边都有一个非负的权重。 每次都可以选择两个不同的顶点u,v,并将路径上每个边的权重减去1。要使所有边的权重变为零。
最小操作数是多少?
您还需要支持边权重的修改:将第p个边的权重更改为w。 每次修改后,您都需要输出答案。

输入描述:

第一行包含两个整数 n q ( 1 n q 1 0 5 ) n,q(1 \leq n,q \leq 10 ^ 5)
在接下来的 n 1 n-1 行中,每行包含三个整数 u v w ( 1 u v n 1 w 1 0 9 ) u,v,w(1 \leq u,v \leq n,1 \leq w \leq 10 ^ 9) ------权重为 w w 的边 ( u v ) (u,v) 。 边缘按顺序从 1 1 索引到 n 1 n-1
在接下来的 q q 行中,每行包含两个整数 p w ( 1 p n 1 1 w 1 0 9 ) p,w(1 \leq p \leq n-1,1 \leq w \leq 10 ^ 9)

输出描述:

输出 q + 1 q + 1 个整数,原始树的答案以及每次修改后的答案。

样例输入:

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 /2
在这里插入图片描述
如果有一条边的权值很大,超过了其他所有边的权值和,那么必然会出现某条边非常多的情况,起点和终点的数量为这条边的数量*2,就像最后一个图形一样。
如果没有超过,那么就会有前两种情况出现。

A C AC C o d e Code:

#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);
	}		
}

猜你喜欢

转载自blog.csdn.net/s260127ljy/article/details/107932575