P1269 信号放大器

链接

P1269 信号放大器

思路

这道题给的是一棵树,信号从 1 号服务器出发,要传到所有节点。信号每经过一条边,强度会减少这条边的长度。如果在某个节点安装信号放大器,就可以把当前仍然大于 0 的信号恢复成初始强度。

目标是让所有节点都能收到信号,并且安装的放大器数量最少。

首先有一个无解判断:如果存在一条边的长度大于等于初始强度 jd,那么无论怎么安装放大器都无法让信号通过这条边。因为信号到达边的另一端时强度必须大于 0,边长等于初始强度时,剩余强度正好为 0,也不合法。

所以代码中先用 dfs 遍历整棵树,同时记录每个节点的父亲:

void dfs(int u,int fa){
    father[u]=fa;
    for(edge edg:G[u]){
        if(edg.id == fa)continue;
        if(edg.val>=jd){
            jdg=true;
        }
        dfs(edg.id,u);
    }
}

如果发现 edg.val >= jd,直接输出 No solution.

接下来考虑最少放多少个放大器。

设:

dp[u]

表示在 u 的子树中,从节点 u 往下走,在不额外从 u 的父边补信号的情况下,最远还需要覆盖到多长的路径。换句话说,dp[u] 记录的是 u 子树中最“危险”的那条向下路径长度。

对于节点 u,先递归处理所有儿子 v。如果从 u 继续往 v 的子树传信号,那么路径长度会变成:

dp[v] + len(u,v)

所以:

dp[u]=max(dp[u],dp[edg.id]+edg.val);

因为只要最远的那条路径能被覆盖,其它更短的路径也能被覆盖。

处理完所有儿子后,还要判断这条最远路径能不能继续通过 u 到父亲的那条边。如果:

dp[u] + uper > jd

说明如果不在 u 这里放一个放大器,那么从父亲方向传来的信号无法覆盖 u 子树中最远的需求。因此必须在 u 安装一个放大器:

if(dp[u]+uper>jd)dp[u]=0,ans++;

安装后,信号在 u 处被恢复成初始强度,所以 u 往下的剩余危险长度重新变成 0

注意这里判断用的是 >,不是 >=。因为这一步表示“父亲方向到 u 后,再继续覆盖子树时是否超过初始强度”。如果刚好等于 jd,到达最远点时强度为 0,这个点已经被收到信号了;而单条边无解判断处必须用 >=,因为边的另一端需要收到强度大于 0 的信号,等于 0 不合法。

最后从根节点 1 开始做树形 DP,答案就是 ans

易错

  1. 无解判断是 edg.val >= jd,不是 edg.val > jd。边长等于初始强度时,信号到达另一端强度为 0,不能被接收。

  2. ddp 中判断是否在当前点放放大器时,用的是:

if(dp[u]+uper>jd)

这里不要随手改成 >=,它和单条边无解判断的含义不一样。

  1. uper 是当前节点 u 到父亲的边长,需要在遍历邻接表时单独记录:
if(edg.id == father[u]){
    uper=edg.val;
    continue;
}
  1. 安装放大器后要把 dp[u] 清零:
dp[u]=0;

因为信号在这里被恢复,往父亲方向看,这个子树已经不再继续累积危险距离。

  1. 输入给的是无根树,所以必须先从 1 号节点 DFS,记录 father,之后树形 DP 才能区分父亲和儿子。

时间复杂度为 O(n),每条边只会被遍历常数次。

代码

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define itn long long 
#define tin long long 
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
int jd;
bool jdg;
const int N=2e4+10;
struct edge{
    itn id,val;
    edge(){}
    edge(int id,int val):id(id),val(val){};
};
vector<edge>G[N];
itn father[N];
int ans;
int dp[N];
void dfs(int u,int fa){
    father[u]=fa;
    for(edge edg:G[u]){
        if(edg.id == fa)continue;
        if(edg.val>=jd){
            jdg=true;
        }
        dfs(edg.id,u);
    }
}
void ddp(int u){
    int uper=0;
    for(edge edg:G[u]){
        if(edg.id == father[u]){
            uper=edg.val;
            continue;
        }
        ddp(edg.id);
        dp[u]=max(dp[u],dp[edg.id]+edg.val);
    }
    if(dp[u]+uper>jd)dp[u]=0,ans++;
    // if(dp[u]>jd)jdg=true;
}
signed main(){
    IOS;
    int n;cin>>n;
    for(itn i=1;i<=n;i++){
        int k;cin>>k;
        for(int j=1;j<=k;j++){
            int id,val;cin>>id>>val;
            G[i].push_back(edge(id,val));
            
        }
        


    }
    cin>>jd;
    dfs(1,0);
    if(jdg){
        cout<<"No solution.";
        return 0;
    }
    ans=0;
    ddp(1);
    cout<<ans;
    return 0;
}

posted @ 2026-05-29 14:35  WHUStar  阅读(12)  评论(0)    收藏  举报