P1269 信号放大器
题意简述
- 给定一棵树(无环连通图),根节点(节点1)是服务器,信号从根节点开始传播。
- 每条边有一个衰减量,信号经过边时强度会减少该衰减量。
- 信号到达节点时必须 > 0 才能被接收和转发。
- 信号放大器可以将当前信号强度恢复到初始强度(从根节点出发时的强度)。
- 目标:用最少的信号放大器,使信号覆盖所有节点。如果无法覆盖,输出
No solution.
。
思路
信号放大器应尽可能晚安装(在必须安装时才安装),以减少数量。
对每个节点,计算其覆盖子树所需的最小信号强度要求(即从该节点到子树中最远叶子的最大衰减量)。
如果父节点传来的信号无法满足子节点的要求,则在子节点安装放大器。
设 $dis[u] $:从节点 uuu 出发,不安装放大器的情况下,覆盖其所有子树所需的最小初始信号强度(即到最远叶子的最大衰减量)。注意:这个值表示:如果信号在 uuu 处强度至少为 dis[u]dis[u]dis[u],则可以覆盖 uuu 的整个子树。
mmm :根节点发出的初始信号强度。
对节点 uuu 遍历其所有子节点 vvv(边权为 www):
- 递归处理子节点:先计算 dis[v]dis[v]dis[v]( vvv 子树的覆盖要求)。
- 判断是否需要安装放大器
若父节点的信号足够覆盖 vvv 子树,即满足
dis[v]+w+1≤m
dis[v] + w + 1 \le m
dis[v]+w+1≤m
那么无需安装信号放大器也能传遍子树。更新:
dis[u]=max(dis[u],dis[v]+w)
dis[u] = max(dis[u],dis[v] + w)
dis[u]=max(dis[u],dis[v]+w)
反之需要在 vvv 上安装信号放大器,此时 vvv 的子树已无需再考虑,只要保证信号能从 uuu 传到 vvv 即可。更新:
dis[u]=max(dis[u],w)
dis[u] = max(dis[u],w)
dis[u]=max(dis[u],w)
如果存在一条边的衰减量 w≥mw ≥ mw≥m,则信号通过该边后强度 $\le 0 $ ,无法传播。输入时记录最大边权 mxmxmx,若 m≤mxm ≤ mxm≤mx,直接输出 “No solution.”
代码实现
#include <bits/stdc++.h>
using namespace std;
const int N = 20010;
int n,m,ans,ma;
int dis[N];//子树内的最长链
struct edges{
int v,w;
};
vector <edges> gra[N];
void read(int &res){
int x = 0,w = 1;
char ch = 0;
while(ch < '0' || ch > '9'){
if(ch == '-') w = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x << 3) + (x << 1) + (ch - '0');
ch = getchar();
}
res = x * w;
}
void dfs(int u,int fa){
for(auto edge : gra[u]){
int v = edge.v;
int w = edge.w;
if(v == fa) continue;
dfs(v,u);
if(dis[v] + w < m){
//信号强度为k时可以传遍子树内所有结点
dis[u] = max(dis[u],dis[v] + w);
}else{
//该点信号强度为k时无法传遍子树内的结点
ans++;
dis[u] = max(dis[u],w);
}
}
}
int main(){
read(n);
int k,u,v,w;
for(u = 1;u <= n;u++){
read(k);
for(int j = 1;j <= k;j++){
read(v);read(w);
gra[u].push_back({v,w});
ma = max(ma,w);
}
}
read(m);
if(ma >= m){
printf("No solution.\n");
return 0;
}
dfs(1,0);
printf("%d\n",ans);
return 0;
}