YbtOJ NOIP2020 模拟赛 B 组 Day10 C. 平衡的树【树形dp】


题目:

传送门


题意:

有一共有 n n n个节点的树,每条边有 a , b a,b a,b两个权值
我们称一棵树为平衡树,当且仅当有边为 ( u , v ) (u,v) (u,v),且 v v v为根的子树内所有 a a a的和不超过 b b b
我们可以选择任意一条边使得该边的 a , b a,b a,b同时 − 1 -1 1,但要保证两者都是非负整数,这样的操作为一点代价
问能否使得这棵树变为平衡树,若可以则输出最少代价


分析:

根据题意,我们需要维护和记录的东西无非两个,某个子树内所有 a a a的和,某个子树内还能操作的次数
前者无脑直接加起来求和,后者一开始有点懵,后来手玩了下,发现其实直接按照题目上保证两者都是非负即可,也是 m i n { a , b } min\{a,b\} min{a,b}
当然, l o n g   l o n g long\ long long long没开的话该见的祖宗一个都别想不见


代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
inline LL read()
{
	LL s=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {s=s*10+c-'0';c=getchar();}
	return s*f;
}
struct node{
	LL to,next,a,b;
}e[400005];
LL ls[200005],cnt=0;
void add(LL x,LL y,LL a,LL b)
{
	e[cnt]=(node){y,ls[x],a,b};
	ls[x]=cnt++;
	return;
}
LL s[200005],o[200005],ans=0,tf=0;
void dfs(LL u,LL fa)
{
	if(tf) return;
	LL s1=0,s2=0;
	for(LL i=ls[u];~i;i=e[i].next)
	{
		LL v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		LL a=e[i].a,b=e[i].b;
		if(s[v]>b)
		{
			if(o[v]<s[v]-b) {tf=1;break;}
			o[v]-=s[v]-b;
			ans+=s[v]-b;
			s[v]=b;
		}
		s1+=a+s[v];s2+=o[v]+min(a,b);
	}
	s[u]=s1;o[u]=s2;
	return;
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	memset(ls,-1,sizeof(ls));
	LL n=read();
	for(LL i=1;i<n;i++)
	{
		LL x=read(),y=read(),a=read(),b=read();
		add(x,y,a,b);add(y,x,a,b);
	}
	dfs(1,0);
	if(tf) printf("-1"); else printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值