鉴于省选杂题题意实在过于复杂QAQ之后就直接上题解了
这题如果想不出来正解的话。。估计只能拿10分,d=1的20分不知道能不能乱搞一发。。后面的几个点毫无区分度啊
令:
f[i][j]表示i这个节点下面j层以下都被覆盖的代价
g[i][j]表示以i这个节点为根的子树、以及向上长度为j的一条链都被覆盖的代价
从下往上dp显然。这个g数组我一开始想到了,因为当前i这个点对上面的影响只有向上长度为d的一条链。但是这个f数组太鬼畜了,后来想一下,当前点所能影响的范围直径为d,那么就要记录d下面的节点。
首先假设当前点x要放守卫,那么如果不放守卫,所更新的范围就是f[x][0-d]和g[x][0-d]。f数组的更新是显然的,只要对一个一个加进来就行了;g数组的方法有点复杂,要在x的孩子中找出一个y,让y伸出一条链覆盖x上面[0-d]个节点,同时x下面的[0-d]个节点也会被覆盖,所以这个时候要加上一个f数组。
状态转移方程就不写了,程序里面还是比较清晰的。
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 2100000000
#define mod 1000000007
#define N 600000
using namespace std;
int n,m,x,y,d,i;
int w[N],b[N],f[N][30],g[N][30];
vector<int> e[N];
void dfs(int x,int fa)
{
int i,j,y;
if (b[x] == 1) f[x][0] = g[x][0] = w[x];
for (i = 1;i <= d; i++) g[x][i] = w[x];
g[x][d+1] = inf;
for (i = 0;i < e[x].size(); i++)
if (e[x][i] != fa)
{
y = e[x][i];
dfs(y,x);
for (j = 0;j <= d; j++) g[x][j] = min(g[x][j]+f[y][j],g[y][j+1]+f[x][j+1]);
for (j = d;j >= 0; j--) g[x][j] = min(g[x][j],g[x][j+1]);
f[x][0] = g[x][0];
for (j = 1;j <= d + 1; j++) f[x][j] += f[y][j-1];
for (j = 1;j <= d + 1; j++) f[x][j] = min(f[x][j],f[x][j-1]);
}
}
int main()
{
scanf("%d%d",&n,&d);
for (i = 1;i <= n; i++) scanf("%d",&w[i]);
scanf("%d",&m);
for (i = 1;i <= m; i++) {scanf("%d",&x); b[x] = 1;}
for (i = 1;i < n; i++)
{
scanf("%d%d",&x,&y);
e[x].push_back(y); e[y].push_back(x);
}
dfs(1,0);
printf("%d",f[1][0]);
return 0;
}