正题
题目链接:https://www.ybtoj.com.cn/contest/70/problem/3
题目大意
一棵树nnn个节点,每条边(x,y,a,b)(x,y,a,b)(x,y,a,b),可以花费111的代价让一条边的a,ba,ba,b都减去111,但是不能小于000,要求最少代价使得每条边满足yyy子树的边的aaa和不超过bbb。
解题思路
发现如果没有不能小于000的条件很好做,这样我们肯定优先减去最下面的aia_iai即可,然后减去上面时bib_ibi减少不会影响答案,因为都已经满足下面的已经减完了。
但是有可能a>ba>ba>b此时aaa的权值就不能减完,对于这些一定不能减去的权值,我们之间让整条到根节点上的bbb减去这些值就好了。
时间复杂度O(n)O(n)O(n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2e5+10,inf=1e18;
struct node{ll to,next;
}a[N*2];
ll n,tot,ls[N],w[N],lim[N],f[N];
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
ll dfs(ll x){ll sum=0,k;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;sum+=dfs(y);}k=max(0ll,w[x]-lim[x]);lim[x]-=sum;w[x]-=k;return sum+k;
}
void solve(ll x){ll tmp=0;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;solve(y);f[x]+=f[y];tmp+=w[y];}f[x]=max(f[x],tmp-lim[x]);w[x]+=tmp;return;
}
int main()
{freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);scanf("%lld",&n);for(ll i=1;i<n;i++){ll x,y,u,v;scanf("%lld%lld%lld%lld",&x,&y,&u,&v);addl(x,y);w[y]=u;lim[y]=v;}lim[1]=inf;dfs(1);for(ll i=1;i<=n;i++)if(lim[i]<0)return printf("-1")&0;solve(1);printf("%lld\n",f[1]);
}