cf 686D - Kay and Snowflake (树的重心)

题目:传送门

思路:显然 ,以 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\n",ans[x]);
74     }
75     return 0;
76 }
View Code

 

posted @ 2020-05-22 15:43  DeepJay  阅读(173)  评论(0编辑  收藏  举报