[CF686D] Kay and Snowflake
阿新 • • 發佈:2020-12-24
題意
給你一棵樹,q個詢問,每次問你一個子樹的重心是誰,如果有多解隨便輸出一個。(N<=300000,Q<=300000)
題解
看資料範圍,每次詢問只能用O(1)得出答案,簡稱需要預處理出每個節點的答案。
先設當v為u的子節點且size(v)*2>size(u),v為u的重兒子。
如果u沒有重兒子,則u的重心就是u。
如果u有重兒子v,則u的重心一定在v與v的重心的路徑上。(我覺得這個思想同樣可以運用到CSP2019樹的重心)
程式碼
#include<cstdio> #include<iostream> #include<cmath> #include<queue> #include<vector> #include<cstring> #include<algorithm> #include<map> #include<set> #include<stack> #include<sstream> #include<string> using namespace std; #define inf 300010 int fa[inf]; vector<int>edge[inf]; int son[inf];//記錄節點數 int ans[inf];//記錄每一個子樹的重心 void dfs(int u)//遍歷找答案 { son[u]=1;//原本只有一個節點 ans[u]=u;//開始時每一個節點的重心就是他自己 int mx=0; // printf("u=%d\n",u); for(int i=0;i<edge[u].size();i++)//找他後面的節點 { int v=edge[u][i]; // printf("v=%d\n",v); dfs(v);//向下遍歷 son[u]+=son[v]; if(son[v]>son[mx]) mx=v;//找到節點數最大的子樹 } if(son[mx]*2>son[u])//重子樹 { int v=ans[mx];//從它的重心開始往根節點找 while((son[u]-son[v])*2>son[u]) v=fa[v];//如果不是重心,就繼續往上 ans[u]=v; } } int main() { int n,q; scanf("%d%d",&n,&q); for(int i=2;i<=n;i++) { scanf("%d",&fa[i]); edge[fa[i]].push_back(i);//fa[i]通向i節點 } dfs(1); while(q--) { int x; scanf("%d",&x); printf("%d\n",ans[x]); } return 0; }