题目
这个题
d
p
dp
dp的状态和大概的方法很好想,但是在付诸实践时却有点。。。
设
f
i
,
j
f_{i,j}
fi,j表示处理完
i
i
i为根的子树后,
i
i
i的子树内没被监视的点中深度最大的离
i
i
i的距离为
j
j
j
g
i
,
j
g_{i,j}
gi,j表示处理完
i
i
i为根的子树后,还可以从
i
i
i往上监视
j
j
j的距离。
可以得到:
g
i
,
j
=
min
u
∈
s
o
n
i
(
g
u
,
j
+
1
+
∑
v
∈
s
o
n
i
a
n
d
v
!
=
u
f
v
,
j
−
1
)
g_{i,j} = \min_{u \in son_i}(g_{u,j+1}+\sum_{v\in son_i \ and \ v!=u } f_{v,j-1})
gi,j=minu∈soni(gu,j+1+∑v∈soni and v!=ufv,j−1)
然后,。。。怎么写啊。。。。。
我们不要再把树形DP的状态简单理解为一个点和一些附加信息了。
树形DP的DP顺序是按照儿子一个一个加入的。
如果不是,像上面的式子会像无形之刃,爆零预定。
我们考虑加入儿子时,设父亲为
u
u
u,加入儿子
v
v
v前的为
f
v
,
u
,
j
f_{v,u,j}
fv,u,j和
g
v
,
u
,
j
g_{v,u,j}
gv,u,j,因为~~可以预见 ~~转移可以原地转移,可以把
v
v
v这一维省掉。
最后变成:
g
u
,
i
=
min
(
g
u
,
i
+
f
v
,
i
−
1
,
f
u
,
i
+
g
v
,
i
+
1
)
g_{u,i} = \min(g_{u,i}+f_{v,i-1},f_{u,i}+g_{v,i+1})
gu,i=min(gu,i+fv,i−1,fu,i+gv,i+1)
f
u
,
i
=
f
u
,
i
+
f
v
,
i
−
1
f_{u,i} = f_{u,i} + f_{v,i-1}
fu,i=fu,i+fv,i−1
其实上面的没有考虑很多情况。
深度最大的不一定是
j
j
j,可以比
j
j
j低。
来一发前缀最小值。
把
f
i
,
j
f_{i,j}
fi,j的意义改为距离
<
=
j
<=j
<=j,
g
i
,
j
g_{i,j}
gi,j的意义改为距离
>
=
j
>=j
>=j
那么。
上面的式子还有情况没有考虑到。
如果子树内没有需要控制的点呢?
我们需要一个
f
i
,
−
∞
f_{i,-\infty}
fi,−∞
将
f
f
f整体右移一位,用
f
i
,
0
f_{i,0}
fi,0存
f
i
,
−
∞
f_{i,-\infty}
fi,−∞就行。
特别注意将儿子考虑在树形DP的状态中,不然DDP怎么打啊。
AC Code:
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define maxn 500002
#define o 22
#define inf 0x3f3f3f3f
using namespace std;
int n,d,m;
int f[maxn][25],g[maxn][25],w[maxn],tag[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=0;
void Node(int u,int v){Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v;}
void dfs(int now,int ff){
if(tag[now]) f[now][0] = g[now][0] = w[now];
for(int i=1;i<=d;i++) g[now][i] = w[now];
g[now][d+1] = inf;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff)
dfs(to[i],now);
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff){
for(int j=d;j>=0;j--) g[now][j] = min(g[now][j]+f[to[i]][j],f[now][j+1]+g[to[i]][j+1]);
for(int j=d;j>=0;j--) g[now][j] = min(g[now][j] , g[now][j+1]);
f[now][0] = g[now][0];
for(int j=1;j<=d+1;j++) f[now][j] +=f[to[i]][j-1];
for(int j=1;j<=d+1;j++) f[now][j] = min(f[now][j] , f[now][j-1]);
}
}
int main(){
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
scanf("%d",&m);
for(int i=1,x;i<=m;i++) scanf("%d",&x),tag[x]=1;
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
Node(u,v),Node(v,u);
}
dfs(1,0);
printf("%d\n",f[1][0]);
}