题目:
题意:
有一共有
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;
}
431

被折叠的 条评论
为什么被折叠?



