# [bzoj1767][Ceoi2009]harbingers【dp】

【题目描述】

## Input

N 以下N-1行A,B,C三个数表示A,B之间有一条长为C的边。 再N行每行两数Wi,Vi 输出有一行N-1个数表示如题所述。

5
1 2 20
2 3 12
2 4 1
4 5 3
26 9
1 10
500 2
2 30

206 321 542 328

## Source

【题解】

树上斜率优化，设dep[j]>dep[k]，当前询问的点为i，若j比k优，

： (f[j]-f[k])/(dep[j]-dep[k])<v[i]


/* --------------
user Vanisher
problem bzoj-1767
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    N       100010
using namespace std;
struct node{
ll data,next,vote;
}e[N*2];
void build(ll u, ll v, ll d){
}
ll tmp=0, fh=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
return tmp*fh;
}
// f[i]=f[j]+dist(i,j)*v[i]+w[i];
// f[i]=f[k]+dist(i,k)*v[i]+w[i];
// f[j]+dist(i,j)*v[i]+w[i]<f[k]+dist(i,k)*v[i]+w[i]
// f[j]-f[j]<dist(j,k)*v[i]
// (f[j]-f[k])/dist(j,k)<v[i] // dep[j]>dep[k]
ll findin(ll nowf, ll nowd){
ll l=2, r=top, p=top+1;
while (l<=r){
ll mid=(l+r)/2;
if ((nowf-stf[mid])*1.0/(nowd-sdep[mid])<(stf[mid]-stf[mid-1])*1.0/(sdep[mid]-sdep[mid-1]))
p=mid, r=mid-1;
else l=mid+1;
}
return p;
}
ll query(ll dep, ll v, ll w){
ll l=2, r=top, p=1;
while (l<=r){
ll mid=(l+r)/2;
if ((stf[mid]-stf[mid-1])*1.0/(sdep[mid]-sdep[mid-1])<v)
p=mid, l=mid+1;
else r=mid-1;
}
return stf[p]+w+(dep-sdep[p])*v;
}
void dfs(ll x, ll fa, ll d){
ll k=findin(f[x],d);
ll olddep=sdep[k], oldf=stf[k], oldtop=top;
sdep[k]=d; stf[k]=f[x]; top=k;
for (ll ed=head[x]; ed!=0; ed=e[ed].next){
if (e[ed].data==fa) continue;
f[e[ed].data]=query(d+e[ed].vote,v[e[ed].data],w[e[ed].data]);
dfs(e[ed].data,x,d+e[ed].vote);
}
sdep[k]=olddep, stf[k]=oldf, top=oldtop;
}
int main(){
for (ll i=1; i<n; i++){
build(u,v,k);
}
for (ll i=2; i<=n; i++)
﻿