1. 程式人生 > 實用技巧 >[CF686D] Kay and Snowflake

[CF686D] Kay and Snowflake

題意

給你一棵樹,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;
}