CSP 2019 Day2 T3 树的重心

题目链接

solution:

首先有这样一个结论
一颗树的重心要么是根节点要么在它重儿子的子树里
这个结论十分显然在这里就不证明了
于是我们第一遍\(dfs\)求出以1节点为根时每个点的重儿子和次重儿子(因为会断掉重儿子的这条边)以及\(pr_{u,i}\)表示从\(u\)节点开始每次沿重儿子跳\(2^i\)步跳到的点的位置
对于第二次\(dfs\),遍历到\(u\)节点时,考虑其儿子节点\(v\),计算将\((u,v)\)断掉后的答案
\(v\)为根节点的子树重心,直接用先前处理的\(pr\)倍增跳同时判断\(size\)即可
\(u\)为根节点的树的重心(相当于换根),从1到\(u\)沿途修改重儿子值即可,倍增方法相同

code:

#include<bits/stdc++.h>
using namespace std;
const int _=3e5+5;
int T,N,Tot;
int Fi[_],Ne[_<<1],To[_<<1];
int Son[_],Sc_Son[_],H[_][20],Sz[_];
long long Ans;
inline int read()
{
	int s=0,w=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
	return s*w;
}
inline void add(int x,int y)
{
	Ne[++Tot]=Fi[x];Fi[x]=Tot;To[Tot]=y;
}
inline void pre(int x)
{
	H[x][0]=Son[x];
	for(int i=1;i<=18;++i)
		H[x][i]=H[H[x][i-1]][i-1];
}
void dfs1(int u,int fa)
{
	int &su=Sz[u],&s=Son[u],&sc=Sc_Son[u];
	su=1;
	for(int i=Fi[u];i;i=Ne[i])
	{
		int v=To[i],&sv=Sz[v];
		if(v==fa)continue;
		dfs1(v,u);
		su+=sv;
		if(sv>=Sz[s]){sc=s;s=v;}
		else if(sv>Sz[sc])sc=v;
	}
	pre(u);
}
inline void get_ans(int x)
{
	if(!Son[x]){Ans+=x;return;}
	int y=x;
	for(int i=18;i>=0;--i)
		if(Sz[x]-Sz[H[y][i]]<Sz[x]/2)y=H[y][i];
	if(Sz[x]-Sz[y]<=Sz[x]/2&&Sz[Son[y]]<=Sz[x]/2)Ans+=y;
	y=Son[y];
	if(Sz[x]-Sz[y]<=Sz[x]/2&&Sz[Son[y]]<=Sz[x]/2)Ans+=y;
}
void dfs2(int u,int fa)
{
	for(int i=Fi[u];i;i=Ne[i])
	{
		int v=To[i];
		if(v==fa)continue;
		int &su=Sz[u],&s=Son[u],sv=Sz[v],_su=Sz[u],_s=Son[u];
		su=N-sv;
		if(v==Son[u])s=Sc_Son[u];
		if(fa&&Sz[fa]>Sz[s])s=fa;
		pre(u);
		get_ans(u),get_ans(v);
		dfs2(v,u);
		su=_su;s=_s;pre(u);
	}
}
int main()
{
	T=read();
	while(T--)
	{
		N=read();
		Ans=Tot=0;fill(Fi+1,Fi+N+1,0);
		for(int i=1;i<N;++i)
		{
			int u=read(),v=read();
			add(u,v),add(v,u);
		}
		fill(Son+1,Son+N+1,0);
		fill(Sc_Son+1,Sc_Son+N+1,0);
		dfs1(1,0);dfs2(1,0);
		printf("%lld\n",Ans);
	}
	return 0;
}
posted @ 2020-11-04 09:42  BILL666  阅读(179)  评论(0)    收藏  举报