欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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。 每次修改后,您都需要输出答案。

输入描述:

第一行包含两个整数nq(1nq105)n,q(1 \leq n,q \leq 10 ^ 5)
在接下来的n1n-1行中,每行包含三个整数uvw(1uvn1w109)u,v,w(1 \leq u,v \leq n,1 \leq w \leq 10 ^ 9)------权重为ww的边(uv)(u,v) 。 边缘按顺序从11索引到n1n-1
在接下来的qq行中,每行包含两个整数pw(1pn11w109)p,w(1 \leq p \leq n-1,1 \leq w \leq 10 ^ 9)

输出描述:

输出q+1q + 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
2020暑期牛客多校训练营第十场(C)Decrement on the Tree(图论,set)
如果有一条边的权值很大,超过了其他所有边的权值和,那么必然会出现某条边非常多的情况,起点和终点的数量为这条边的数量*2,就像最后一个图形一样。
如果没有超过,那么就会有前两种情况出现。

ACAC CodeCode:

#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);
	}		
}
相关标签: 图论 set