题目:传送门
思路:显然 ,以 u 为根节点的子树的重心(设为ans[u] ),一定是在 ans[v] 到 u 的路径上,其中 v 是 u 的重儿子。
最暴力的思路,便是枚举路径上的所有点,但这样会出现大量的重复枚举,例如当树退化成线性结构,这种做法的复杂度也会退化成O(n2)。所以,这个时候可以利用重心的一个性质:以重心为根,所有子树的重量均<=原树重量/2 (重子树的重量<=原树重量/2)
那么,复杂度就可以优化到O(n) ,因为对于每个点重儿子只有一个,且枚举不重复。
代码:

1 //#include<bits/stdc++.h> 2 3 #include<cstdio> 4 #include<cmath> 5 #include<cstring> 6 #include<vector> 7 #include<cctype> 8 #include<queue> 9 #include<algorithm> 10 #include<map> 11 #include<set> 12 13 #pragma GCC optimize(2) 14 using namespace std; 15 typedef long long LL; 16 typedef pair<int,int> pii; 17 typedef pair<double,double> pdd; 18 const int N=1e6+5; 19 const int M=1e4+5; 20 const int inf=0x3f3f3f3f; 21 const LL mod=1e9+7; 22 const double eps=1e-9; 23 const long double pi=acos(-1.0L); 24 #define ls (i<<1) 25 #define rs (i<<1|1) 26 #define fi first 27 #define se second 28 #define pb push_back 29 #define mk make_pair 30 #define mem(a,b) memset(a,b,sizeof(a)) 31 LL read() 32 { 33 LL x=0,t=1; 34 char ch; 35 while(!isdigit(ch=getchar())) if(ch=='-') t=-1; 36 while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); } 37 return x*t; 38 } 39 vector<int> e[N]; 40 int son[N],cnt[N],f[N],ans[N]; 41 void dfs(int u,int pre) 42 { 43 cnt[u]=1; 44 f[u]=pre; 45 for(auto v:e[u]) 46 { 47 if(v==pre) continue; 48 dfs(v,u); 49 cnt[u]+=cnt[v]; 50 if(cnt[son[u]]<cnt[v]) son[u]=v; 51 } 52 ans[u]=u; 53 if(son[u]) 54 { 55 int p=ans[son[u]]; 56 while(p!=u&&(cnt[u]-cnt[p])*2>cnt[u]) p=f[p]; 57 ans[u]=p; 58 } 59 } 60 int main() 61 { 62 int n=read(),m=read(); 63 for(int i=2;i<=n;i++) 64 { 65 int x=read(); 66 e[x].pb(i); 67 e[i].pb(x); 68 } 69 dfs(1,0); 70 for(int i=1;i<=m;i++) 71 { 72 int x=read(); 73 printf("%d ",ans[x]); 74 } 75 return 0; 76 }