DP
\(F_{i,j}\) 表示处理掉以 \(i\) 为根的子树,并向上支援 \(j\) 格的最小代价
\(j\in [-D,D]\)
暴力转移 \(O(ND^3)\) ~ \(O(ND^2)\)
稍作修改
\(F_{i,j}\) 表示处理掉以 \(i\) 为根的子树,并向上支援至少 \(j\) 格的最小代价
容易得到 \(O(ND)\) 做法
#include <cstdio>
#include <vector>
#include <algorithm>
using std::min;
using std::vector;
const int MAXN=500111;
const int MAXD=23;
const int INF=1034567890;
int N, D, M;
struct Vert{
int Val;
bool Is;
int FE;
int F_[MAXD<<1], *F;
Vert(){F=F_+MAXD;}
} V[MAXN];
struct Edge{
int y, next;
} E[MAXN<<1];
int Ecnt;
void addE(int a, int b){
++Ecnt;
E[Ecnt].y=b;E[Ecnt].next=V[a].FE;V[a].FE=Ecnt;
}
void DFS(int at, int f){
vector<int> Son;
for(int k=V[at].FE, to;k;k=E[k].next){
to=E[k].y;
if(to==f) continue;
Son.push_back(to);
DFS(to, at);
}
if(!Son.size()){
for(int i=1-V[at].Is;i<=D;++i) V[at].F[i]=V[at].Val;
return;
}
//j=D;
for(int i=0, s=Son.size();i<s;++i){
V[at].F[D]+=V[Son[i]].F[-D];
}
V[at].F[D]+=V[at].Val;
//j=[1,D);
for(int j=!V[at].Is, t;j<D;++j){
t=0;
for(int i=0, s=Son.size();i<s;++i){
t+=V[Son[i]].F[-j];
}
V[at].F[j]=V[Son[0]].F[j+1]-V[Son[0]].F[-j]+t;
for(int i=1, s=Son.size();i<s;++i){
V[at].F[j]=min(V[at].F[j], V[Son[i]].F[j+1]-V[Son[i]].F[-j]+t);
}
}
//j=0;
if(!V[at].Is)
for(int i=0, s=Son.size();i<s;++i)
V[at].F[0]+=V[Son[i]].F[0];
//j=-1;
if(!V[at].Is) V[at].F[-1]=INF;
//j=[-D,-1);
for(int j=-2+V[at].Is;j>=-D;--j){
for(int i=0, s=Son.size();i<s;++i)
V[at].F[j]+=V[Son[i]].F[j+1];
}
for(int i=D-1;i>=-D;--i) V[at].F[i]=min(V[at].F[i+1], V[at].F[i]);
}
int main(){
scanf("%d%d", &N, &D);
for(int i=1;i<=N;++i) scanf("%d", &V[i].Val);
scanf("%d", &M);
for(int i=1, a;i<=M;++i) {scanf("%d", &a);V[a].Is=true;}
for(int i=1, a, b;i<N;++i){
scanf("%d%d", &a, &b);
addE(a, b);addE(b, a);
}
DFS(1, 0);
printf("%d\n", V[1].F[0]);
return 0;
}
/*
12 2
8 9 12 6 1 1 5 1 4 8 10 6
10
1 2 3 5 6 7 8 9 10 11
1 3
2 3
3 4
4 5
4 6
4 7
7 8
8 9
9 10
10 11
11 12
10
*/