####题目描述
q q q只乌鸦要坐机到同一个地方看阿福揍扁成龙!坐飞机就要到飞机场,一共 n n n个飞机场构成了一棵树,编号为 1 1 1到 n n n,其中 1 1 1号点是所有乌鸦的目的地,所以可以把飞机场的结构视为一棵以 1 1 1号点为根的有根树。坐飞机就要买飞机票,每个机场都只售 1 1 1种飞机票,我们用 v , d v,d v,d两个参数描述一张飞机票,表示这张飞机票售价为 v v v,可以让乌鸦飞到从当前机场到 1 1 1号机场的路径上距离不超过 d d d的任意 1 1 1个机场(两个机场的距离定义为两点之间简单路径上的边数)。
由于一些规定,一只乌鸦同时只能持有至多一张飞机票,每张飞机票只能使用一次。每只乌鸦都很理性,不在乎转多少趟飞机,只要总费用最少就行。请你计算每只乌鸦的最小总费用。
f
[
x
]
f[x]
f[x]表示从
x
x
x号点出发到达根节点的最小总费用,那么转移如下(
y
y
y是
x
x
x的祖先):
f
[
x
]
=
m
i
n
{
f
[
y
]
}
+
v
[
x
]
f[x]=min\{f[y]\}+v[x]
f[x]=min{f[y]}+v[x]
而如果我们暴力找出最小的 f [ y ] f[y] f[y]的话,时间复杂度在 O ( l o g 2 2 n ) − > O ( n 2 ) O(log_2^2n)->O(n^2) O(log22n)−>O(n2)级别,超级不稳定,所以我们考虑优化求最小值的过程。
注意到这是在树上进行 D P DP DP,于是我们可以用倍增来维护这个最小值(作为一个连 S t St St表都用线段树代替的蒟蒻确实是第一次除了倍增 L C A LCA LCA以外写倍增了)。
代码实现其实就与倍增求 L C A LCA LCA十分类似了:
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long
#define For(I,X,Y) for(LL I=(X);I<=(Y);I++)
#define ForDown(I,X,Y) for(LL I=(X);I>=(Y);I--)
using namespace std;
inline LL Read(){
LL X=0;char CH=getchar();bool F=0;
while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
return F?-X:X;
}
const LL Max=3e5+5;
const LL Inf=1e18;
LL DP[Max];
LL N,M,V[Max],D[Max];
LL Crow,Fa[Max][20],Dis[Max][20];
LL Cnt,To[Max<<1],Next[Max<<1],Head[Max];
#define Update(X,Y) ((X)=(X)>(Y)?(Y):(X))
#define ReNew(X,Y,Z) ((X)=(Z)>(Y)?(Y):(Z))
#define Search(X,I) for(LL I=Head[X];I;I=Next[I])
void Insert(LL X,LL Y){Cnt++;To[Cnt]=Y;Next[Cnt]=Head[X];Head[X]=Cnt;}
void DFS(LL X){
For(I,1,19) Fa[X][I]=Fa[Fa[X][I-1]][I-1],ReNew(Dis[X][I],Dis[X][I-1],Dis[Fa[X][I-1]][I-1]);
if(X!=1){
LL Loc=X,Delta=D[X];
DP[X]=Inf;
For(I,0,19){
if(Delta&(1<<I)){
Update(DP[X],Dis[Loc][I]);Loc=Fa[Loc][I];
}
}
DP[X]+=V[X];
}
Search(X,I){
LL Y=To[I];
if(Y!=Fa[X][0]){
Fa[Y][0]=X;Dis[Y][0]=DP[X];DFS(Y);
}
}
}
int main(){
N=Read();
For(I,1,N-1){
LL X=Read(),Y=Read();
Insert(X,Y);Insert(Y,X);
}
For(I,1,N) V[I]=Read(),D[I]=Read();
DFS(1);
M=Read();
For(I,1,M) printf("%lld\n",DP[Read()]);
return 0;
}