P3267题解
[JLOI2016/SHOI2016] 侦察守卫
总结
树形 dp 套路:可以设状态为 $ dp_{x,p} $, p 表示限制。
类似这种子树状态会影响父亲的情况,我们可以列两个不同的状态分步处理子树和向上的影响。
题目大意
给定一颗树,树上有 \(m\) 个点需要被照亮,我们可以在每个点 \(x\) 以 \(w_x\) 的代价放置一个能照亮距离小于等于 \(d\) 的所有点的灯,求最小代价。
题目分析
我们可以设 \(f_{x,i}\) 为 \(x\) 第子树向下第 \(i\) 层以下所有点都已满足条件,前 \(i\) 层任意的最小代价,\(g_{x,i}\) 为 \(x\) 的子树已全处理完,向上覆盖了 \(i\) 级祖先。可以列出转移:
\[g_{x,i}=\min{g_{x,i}+f_{y,i},g_{y,i+1}+f_{x,i+1}}\\
g_{x,i}=\min{g_{x,i},g{x,i+1}}\\
f_{x,i}=\sum f_{son_x,i-1}\\
f_{x,i}=\min{f_{x,i-1}}
\]
直接背包即可。
代码
#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=5e5+5;
const int inf=1e9;
int n,d,m;
int w[N];
vector<int>e[N];
int vis[N];
int f[N][25],g[N][25];
void dfs(int x,int fa)
{
if(vis[x])f[x][0]=g[x][0]=w[x];
for(int i=1;i<=d;i++)g[x][i]=w[x];
g[x][d+1]=inf;
for(int y:e[x])
{
if(y==fa)continue;
dfs(y,x);
for(int i=d;i>=0;i--)g[x][i]=min(g[x][i]+f[y][i],g[y][i+1]+f[x][i+1]);
for(int i=d;i>=0;i--)g[x][i]=min(g[x][i],g[x][i+1]);
f[x][0]=g[x][0];
for(int i=1;i<=d+1;i++)f[x][i]+=f[y][i-1];
for(int i=1;i<=d+1;i++)f[x][i]=min(f[x][i],f[x][i-1]);
}
}
int main()
{
cin>>n>>d;
for(int i=1;i<=n;i++)cin>>w[i];
cin>>m;
for(int i=1;i<=m;i++)
{
int x;cin>>x;
vis[x]=1;
}
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1,0);
cout<<f[1][0];
}

浙公网安备 33010602011771号