LGP12449 [COTS 2025] 吸尘 Usisavač 学习笔记
LGP12449 [COTS 2025] 吸尘 Usisavač 学习笔记
前言
喜欢降蓝的小朋友们你们好啊。
题意简述
给定一棵根为 \(1\) 的树,每个结点上有一个插座。假设你有一个吸尘器,它有一根长为 \(r\) 的电缆,当你将它插到一个插座上你就可以使用它了,当然,插座与吸尘器之间的边数不能超过 \(r\)。
你从根节点出发,每一分钟开始你都待在某个结点,可以进行以下操作:
- 若吸尘器未通电,你可以:
- 将它插到当前结点的插座上。
- 手持它移动到相邻房间,耗时一分钟。
- 若吸尘器已通电,你可以:
- 拔下插头,如果可能。
- 移动到相邻房间并清理走廊,耗时一分钟。前提是你所在的位置离当前使用的插座之间的边数不大于 \(r\)。
现在有 \(m\) 个吸尘器,它们之间唯一的不同在于 \(r\) 的长度。对于每一个 \(r\) 求出:清理完整棵树所需的最短时间。
\(n,m\le 3\times 10^5\)。
做法解析
你先考虑对于一个固定的 \(r\) 怎么办。这道题里面大多数元素都有很强的普遍性,所以不妨考虑两个 \(r\) 取极端值的案例。
\(r\) 很大的时候答案显然是 \(2(n-1)-d\),其中 \(d\) 为以 \(1\) 为端点的最长链的长度。这个显然,因为你最后并不用回到 \(1\)。
\(r=1\) 的时候,你发现你开着吸尘器走的路还是 \(2(n-1)-d\) 的,但是你多了不少关着吸尘器要走的路程。你发现所有和叶子结点相邻接的点和根节点都是我们必然会关着吸尘器要到达的点。也就是说我们关吸尘器走的路就是把所有叶子结点(不考虑根)剪掉之后剩余的边数。
接下来不需要我教你怎么做了吧。有疑问看代码。
代码实现
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=3e5+5;
int N,M,X,Y,deg[MaxN];
vecint Tr[MaxN];
void addudge(int u,int v){
Tr[u].push_back(v);
Tr[v].push_back(u);
deg[u]++,deg[v]++;
}
int dep[MaxN],mxd;
void dfs(int u,int f){
dep[u]=dep[f]+1,maxxer(mxd,dep[u]);
for(auto v : Tr[u])if(v!=f)dfs(v,u);
}
int ans[MaxN];
void solve(){
vecint cvec;
for(int i=2;i<=N;i++)if(deg[i]==1)cvec.push_back(i);
for(int i=1,csum=N;i<=N;i++){
vecint nvec;
for(int u : cvec){
for(auto v : Tr[u]){
if(deg[v]==-1)continue;deg[v]--;
if(deg[v]==1&&v!=1)nvec.push_back(v);
}
deg[u]=-1,csum--;
}
cvec=nvec,ans[i]=2*(N-1)+2*(csum-1)-(mxd-1);
}
}
int main(){
readis(N,M);
for(int i=1;i<N;i++)readis(X,Y),addudge(X,Y);
dfs(1,0);solve();
for(int i=1;i<=M;i++)readi(X),writip(ans[X]);
return 0;
}
浙公网安备 33010602011771号