这题,主要是想清楚每个点的过程
对于一个点,他能走完,当且仅当,流向它的权值和小于它流出的权值
那么对于每一个点,总有一个流完的时间点
那么接下来的问题就是,那如果走不完呢,出减入变成负的了咋整
没事,他的儿子总有流完的时候,等到他儿子流完了,他父亲被流入的就少了,也就有了新的流完时间点。
那么怎么得出这个时间点呢?
我们把他和他父亲缩在一起,形成一个新的点,我们考虑这个新点的出入量是什么,因为他的儿子流完了,所以流入应小于流出,所以等于每秒贡献的为他自己被流入的量。
所以,新点的净流出应该为
他的父亲的净流出量减去此儿子的流入量加儿子的流出量
清楚一点
新
点
每
时
间
减
少
的
=
出
父
亲
+
出
流
完
的
儿
子
−
入
儿
子
1
−
>
n
(
包
括
流
完
的
儿
子
)
−
入
流
完
的
儿
子
的
所
有
流
入
新点每时间减少的=出_{父亲}+出_{流完的儿子}-入_{儿子1->n(包括流完的儿子)}-入_{流完的儿子的所有流入}
新点每时间减少的=出父亲+出流完的儿子−入儿子1−>n(包括流完的儿子)−入流完的儿子的所有流入
流完的儿子流出的当然等于从流完的儿子流向父亲的,是一个东西嘛,所以实际上为
出
父
亲
−
入
儿
子
1
−
>
n
(
不
包
括
流
完
的
儿
子
)
−
入
流
完
的
儿
子
的
所
有
流
入
出_{父亲} - 入_{儿子1->n(不包括流完的儿子)}-入_{流完的儿子的所有流入}
出父亲−入儿子1−>n(不包括流完的儿子)−入流完的儿子的所有流入
用父子流量和除以新的净流出,就得出了上面提到的新时间点
这样我们就可以不断的按时间节点更新了
用并查集维护父子合并即可
因为询问不一定按照时间顺序, 记得两次排序
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define INF 2147483647
using namespace std;
const int maxn = 2e5+7;
struct node{
int pos;
long long t,ans;
}task[maxn];
bool cmp1(node a,node b){
return a.t<b.t;
}
bool cmp2(node a,node b){
return a.pos<b.pos;
}
int f[maxn],fa[maxn];
long long cow[maxn],flow[maxn],pass[maxn];
int n,m;
priority_queue< pair<long long,int> >q;
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=2;i<=n;i++){
scanf("%d%lld%lld",&fa[i],&cow[i],&flow[i]);
pass[fa[i]]-=flow[i];
pass[i]+=flow[i];
}
for(int i=1;i<=m;i++){
scanf("%lld",&task[i].t);
task[i].pos=i;
}
sort(task+1,task+1+m,cmp1);
for(int i=2;i<=n;i++){
if(pass[i]>0)q.push(make_pair(-(cow[i]/pass[i]),i));
}
int l=1;
while(!q.empty()&&l<=m){
while(l<=m&&task[l].t<=-q.top().first){
task[l].ans=cow[1]-pass[1]*task[l].t,l++;
}
if(f[q.top().second]!=q.top().second){q.pop();continue;}
int f1=q.top().second,f2=find(fa[q.top().second]);q.pop();
cow[f2]+=cow[f1];pass[f2]+=pass[f1];
if(pass[f2]>0)q.push(make_pair(-cow[f2]/pass[f2],f2));
f[f1]=f2;
}
sort(task+1,task+1+m,cmp2);
for(int i=1;i<=m;i++)printf("%lld\n",task[i].ans);
return 0;
}