【CF1182D】Complete Mirror【树的重心】

传送门

题意:给一棵 N N N个结点的树,你需要钦定一个根,使得所有深度相同的点的度数相同。

N ≤ 100000 N \leq 100000 N100000

用脑子想一想,就是根节点直接相连的子树都长得一模一样。

如果根节点度数大于1,我们发现它把整棵树均匀地分成了若干份。所以根节点是重心。

O ( N ) O(N) O(N)找重心检查一下

如果根节点度数等于1,也就是拉了一条链下去

由于是递归的,所以走到有岔路的地方就是岔路口所在子树的重心

因为两棵树合并后的重心在原来的重心的路径上,所以整棵树的重心在链上。

所以沿一条链走到底就可以了。

但如果有多条路,说明重心是岔路口。因为下面长得一模一样,所以即使是链,长度也都相同。

所以找两条长度不同的链的顶部搜一下即可。

复杂度 O ( N ) O(N) O(N)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#define MAXN 100005
#define MAXM 200005
using namespace std;
struct edge{int u,v;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt;
void addnode(int u,int v)
{
	e[++cnt]=(edge){u,v};
	nxt[cnt]=head[u];
	head[u]=cnt;
}
int siz[MAXN],dep[MAXN],n;
void dfs(int u)
{
	siz[u]=1;
	for (int i=head[u];i;i=nxt[i])
		if (!dep[e[i].v])
		{
			dep[e[i].v]=dep[u]+1;
			dfs(e[i].v);
			siz[u]+=siz[e[i].v];
		}
}
int maxp[MAXN]={0x7fffffff};
int findroot()
{
	dfs(dep[1]=1);
	int rt=0;
	for (int u=1;u<=n;u++)
	{
		for (int i=head[u];i;i=nxt[i])
			if (dep[e[i].v]==dep[u]+1)
				maxp[u]=max(maxp[u],siz[e[i].v]);
		if (n-siz[u]>maxp[u]) maxp[u]=n-siz[u];
		if (maxp[u]<maxp[rt]) rt=u;
	}
	return rt;
}
int tmp[MAXN];
bool check(int rt)
{
	memset(siz,0,sizeof(siz));
	memset(dep,0,sizeof(dep));
	memset(tmp,0,sizeof(tmp));
	dep[rt]=1;dfs(rt);
	for (int u=1;u<=n;u++)
	{
		int deg=0;
		for (int i=head[u];i;i=nxt[i])
			++deg;
		if (!tmp[dep[u]]) tmp[dep[u]]=deg;
		if (tmp[dep[u]]!=deg) return false;
	}
	return true;
}
int line(int u,int f)
{
	if (!nxt[head[u]])	return u;
	if (nxt[nxt[head[u]]]) return 0;
	int i=head[u];
	if (e[i].v==f) i=nxt[i];
	return line(e[i].v,u);
}
int len[MAXN];
inline bool cmp(const int& a,const int& b){return dep[a]<dep[b];}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		addnode(u,v);addnode(v,u);
	}
	int rt=findroot();
	if (check(rt))
	{
		printf("%d\n",rt);
		return 0;
	}
	for (int i=head[rt];i;i=nxt[i])
		len[++len[0]]=line(e[i].v,rt);
	sort(len+1,len+len[0]+1,cmp);
	if (len[1]&&check(len[1]))
	{
		printf("%d\n",len[1]);
		return 0;
	}
	if (len[len[0]]&&check(len[len[0]]))
	{
		printf("%d\n",len[len[0]]);
		return 0;
	}
	puts("-1");
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值