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

https://ac.nowcoder.com/acm/contest/5675/C

每条边有一些权值,每次选择一条路径全部减1,最多选多少条路径。我们把问题转换为最少多少个路径的端点,路径数就是段点数/2,对于一个点,我们考虑他连接的所有边,如果mx<=sum/2,那么只要两条边能匹配起来,他本身就不用作为一个端点,那么此时作为端点的数量就是sum&1,如果mx>sum/2,那么最大的那条边是匹配不完的,作为端点的数量就是2*mx-sum。

修改维护的话就用个multiset搞搞就好

#include
using namespace std;
typedef long long ll;

const int maxl=1e5+10;

int n,q;ll ans;
struct ed
{
	int u,v,w;
}e[maxl];
ll sum[maxl];
multiset s[maxl];

inline void prework()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
		sum[e[i].u]+=e[i].w;
		sum[e[i].v]+=e[i].w;
		s[e[i].u].insert(e[i].w);
		s[e[i].v].insert(e[i].w);
	}
}

inline int calc(int i)
{
	int w=(*s[i].rbegin());
	if(w<=sum[i]/2)
		return sum[i]&1;
	return 2*w-sum[i]; 
}

inline void mainwork()
{
	ans=0;int p,w;
	for(int i=1;i<=n;i++)
		ans+=calc(i);
	printf("%lld\n",ans/2);
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&p,&w);
		ans-=calc(e[p].u);ans-=calc(e[p].v);
		s[e[p].u].erase(s[e[p].u].lower_bound(e[p].w));
		s[e[p].v].erase(s[e[p].v].lower_bound(e[p].w));
		sum[e[p].u]-=e[p].w;sum[e[p].v]-=e[p].w;
		e[p].w=w;
		sum[e[p].u]+=e[p].w;sum[e[p].v]+=e[p].w;
		s[e[p].u].insert(w);s[e[p].v].insert(w);
		ans+=calc(e[p].u);ans+=calc(e[p].v);
		printf("%lld\n",ans/2);
	}
}

int main()
{
	prework();
	mainwork();
	return 0;
}

 

你可能感兴趣的:(贪心)