我们设计状态
g[i][j]表示当前在i节点,处理到了第k棵子树时,还能向外扩展j层的最小花费。
f[i][j]表示当前在i节点,处理到了第k棵子树时,在i节点的子树中向下还有j层没有被覆盖时的最小花费。
那么状态转移(现在在点u,v为新子树):
g[u][j]=min(g[u][j]+f[v][j],g[v][j+1]+f[u][i+1])
意思分别是:
u点向上还要覆盖j层的话,也就是原来就可以向上覆盖j层,加上这棵新的子树的花费
u点向上还要覆盖j层的话,也就是这棵新子树可以向上覆盖j+1层,加上原来子树的花费
那么代码如下:
#include<bits/stdc++.h>
#define N 500007
#define inf 0x3f3f3f3f
using namespace std;
int n,m,dis,cnt;
int val[N],head[N],f[N][21],g[N][21];
bool vis[N];
struct Edge
{
int to,nxt;
}edge[N<<1];
void Add(int u,int v)
{
edge[++cnt]=(Edge){v,head[u]};
head[u]=cnt;
}
void Dfs(int u,int fa)
{
if(vis[u])
f[u][0]=g[u][0]=val[u];
for(int i=1;i<=dis;++i)
g[u][i]=val[u];
g[u][dis+1]=inf;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==fa)
continue;
Dfs(v,u);
for(int j=0;j<=dis;++j)
g[u][j]=min(g[u][j]+f[v][j],g[v][j+1]+f[u][j+1]);
for(int j=dis;j>=0;--j)
g[u][j]=min(g[u][j],g[u][j+1]);
f[u][0]=g[u][0];
for(int j=1;j<=dis+1;++j)
f[u][j]+=f[v][j-1];
for(int j=1;j<=dis+1;++j)
f[u][j]=min(f[u][j],f[u][j-1]);
}
}
int main()
{
scanf("%d%d",&n,&dis);
for(int i=1;i<=n;++i)
scanf("%d",&val[i]);
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
int in;
scanf("%d",&in);
vis[in]=true;
}
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
Add(u,v);
Add(v,u);
}
Dfs(1,0);
printf("%d",f[1][0]);
return 0;
} 


京公网安备 11010502036488号