题解:P12534 [XJTUPC 2025] 奥日
思路
题意是要你在树上走一条非简单路径,使得遍历到的点数量最多,且路径经过的总点数与其差值不超过
假设你已经知道要遍历哪一个连通块了,你肯定也知道怎么走最优:找到直径,从它的一端开始 DFS 遍历,最后到直径另一端结束。
这样为何最优?因为如果要回到出发点
注意到,题目的限制是
最后把你选出的连通块跑一遍 DFS,保证直径最后遍历,即可得到答案。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
vector<int> T[maxn];
static inline int read(){
int x = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
int dep[maxn], fa[maxn], vis[maxn];
void diameter(int x, int f, int &p){
dep[x] = dep[f] + 1, fa[x] = f;
if(dep[x] > dep[p]) p = x;
for(int i = 0; i < T[x].size(); i ++){
int j = T[x][i];
if(j == f) continue;
diameter(j, x, p);
}
}
int ans[maxn << 2], ant = 0;
bool print(int x, int f, int &rest){
if(!vis[x]){
if(!rest) return false;
rest --;
}
ans[++ ant] = x;
int tag = 0;
for(int i = 0; i < T[x].size(); i ++){
int j = T[x][i];
if(j == f) continue;
if(vis[j]){
tag = j;
continue;
}
bool fl = print(j, x, rest);
if(fl) ans[++ ant] = x;
}
if(tag) print(tag, x, rest);
return true;
}
int main(){
int TT = read();
while(TT --){
int n = read(), k = read();
for(int i = 1; i < n; i ++){
int u = read(), v = read();
T[u].push_back(v), T[v].push_back(u);
}
int a = 0, b = 0;
diameter(1, 0, a), diameter(a, 0, b);
for(int i = b; i; i = fa[i]) vis[i] = 1;
int D = dep[b], rest = k;
print(a, 0, rest);
printf("%d\n", ant);
for(int i = 1; i <= ant; i ++) printf("%d ", ans[i]);
printf("\n");
ant = 0;
for(int i = 1; i <= n; i ++) T[i].clear(), vis[i] = dep[i] = fa[i] = 0;
}
return 0;
}
一开始看成每个点经过不超过