题意:给你一棵树,q个询问,每个询问求从v出发,总步数小于等于k的时候的能走到的最多的点(每个点只算一次)。
题解:可以确定的是走过的路径肯定是一条路径上的边只走过一次+其他走过边都经过两次的情况,所以我们只需要找到距离当前点最远的点,他们之间只走过一次,其他边都计算为两次即可,找树的直径的两端点可以直接找到最远的点在哪里。
AC代码:
#include<stdio.h> #include<string.h> #include<vector> #define N 100005 using namespace std; vector<int>vt[N]; int dis1[N],dis2[N]; void dfs(int u,int fa) { for(int i=0;i<vt[u].size();i++) { int to=vt[u][i]; if(to==fa)continue; dis1[to]=dis1[u]+1; dfs(to,u); } } int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); for(int i=1;i<=n;i++)dis1[i]=dis2[i]=0,vt[i].clear(); for(int i=0;i<n-1;i++) { int u,v; scanf("%d%d",&u,&v); vt[u].push_back(v); vt[v].push_back(u); } dfs(1,1); int s1,s2,ma=-1; for(int i=1;i<=n;i++) if(ma<dis1[i]) ma=dis1[i],s1=i; for(int i=1;i<=n;i++)dis1[i]=0; dfs(s1,s1); ma=-1; for(int i=1;i<=n;i++) if(ma<dis1[i]) ma=dis1[i],s2=i; for(int i=1;i<=n;i++)dis2[i]=dis1[i],dis1[i]=0; dfs(s2,s2); int q; scanf("%d",&q); while(q--) { int u,k; scanf("%d%d",&u,&k); int dist=max(dis1[u],dis2[u]); if(dist>=k)printf("%d\n",k+1); else printf("%d\n",min(n,dist+(k-dist)/2+1)); } } }