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];
}
posted @ 2026-10-10 19:58  tidewave  阅读(7)  评论(0)    收藏  举报