Loading [MathJax]/jax/element/mml/optable/BasicLatin.js

[CF842E]Nikita and game

[CF842E]Nikita and game

题目链接:

CF842E

博客地址:

[CF842E]Nikita and game - skylee

题目大意:

一棵树初始只有一个编号为1的根结点。n(n3×105)次操作,每次新增一个点作为pi的子结点,询问更新后有多少点可以作为树直径的端点。

思路:

根据直径的一些性质,不难发现所有直径的相交部分一定是连续的一段。我们可以由此将所有可以作为端点的点分为三类:

  1. 在连续段左边的点
  2. 在连续段右边的点
  3. 无所谓左边和右边的点(此时相交部分变成了一个点)

设集合S1中为1类点,集合S2中为2类点,而3类点归为其中任意一个集合都可以。不难发现,从S1,S2中分别随机选取一个点,两个点之间的路径一定是树的直径。

每次新加入一个点后,分别与S1S2中任意一个点求距离,记为d1,d2。记当前树的直径为maxd,新加入的点为i。若max,则直径可以被更新。若d_1是新的maxd,则新的S_2=\{i\}。但原S_2中的元素并不一定全部失效,对于那些原本应当归为第3类的点j,若dis(i,j)=d_1,则将其归为S_1中。若d_2是新的maxd同理。

\max(d_1,d_2)=maxd,将i加入到相应的点集即可。

点集S_1,S_2具有无序性,用std::vector即可实现(并不需要用std::set),插入和清空都是\mathcal O(1),而LCA求距离时间复杂度\mathcal O(\log n)。总的时间复杂度为\mathcal O(n\log n)。而那些用std::set的虽然同样也是\mathcal O(n\log n),但常熟要大不少,所以我的std::vector轻松跑到Rank2,比那些std::set不知道快到哪里去了(Rank1手写数组)。

源代码:

#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
inline int getint() {
	register char ch;
	while(!isdigit(ch=getchar()));
	register int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x; 
}
const int N=3e5+2,logN=19;
int dep[N],anc[N][logN];
inline int lg2(const float &x) {
	return ((unsigned&)x>>23&255)-127;
}
inline void add_vertex(const int &x,const int &par) {
	dep[x]=dep[anc[x][0]=par]+1;
	for(register int i=1;i<=lg2(dep[x]);i++) {
		anc[x][i]=anc[anc[x][i-1]][i-1];
	}
}
inline int lca(int x,int y) {
	if(dep[x]<dep[y]) std::swap(x,y);
	for(register int i=lg2(dep[x]-dep[y]);i>=0;i--) {
		if(dep[anc[x][i]]>=dep[y]) x=anc[x][i];
	}
	for(register int i=lg2(dep[x]);i>=0;i--) {
		if(anc[x][i]!=anc[y][i]) {
			x=anc[x][i];
			y=anc[y][i];
		}
	}
	return x==y?x:anc[x][0];
}
inline int dist(const int &x,const int &y) {
	return dep[x]+dep[y]-dep[lca(x,y)]*2;
}
std::vector<int> s1,s2;
int main() {
	add_vertex(1,0);
	s1.push_back(1);
	const int n=getint()+1;
	s1.reserve(n);
	s2.reserve(n);
	for(register int i=2,maxd=0;i<=n;i++) {
		add_vertex(i,getint());
		int d1=s1.empty()?0:dist(i,s1[0]);
		int d2=s2.empty()?0:dist(i,s2[0]);
		if(std::max(d1,d2)>maxd) {
			maxd=std::max(d1,d2);
			if(maxd==d1) {
				for(register int &x:s2) {
					if(dist(x,i)==d1) {
						s1.push_back(x);
					}
				}
				s2.clear();
			} else {
				for(register int &x:s1) {
					if(dist(x,i)==d2) {
						s2.push_back(x);
					}
				}
				s1.clear();
			}
		}
		if(std::max(d1,d2)==maxd) {
			(maxd==d1?s2:s1).push_back(i);
		}
		printf("%lu\n",s1.size()+s2.size());
	}
	return 0;
}
posted @   skylee03  阅读(219)  评论(1编辑  收藏  举报
编辑推荐:
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
阅读排行:
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
历史上的今天:
2017-05-24 [USACO09OPEN]捉迷藏Hide and Seek
2017-05-24 [洛谷3371]【模板】单源最短路径
点击右上角即可分享
微信分享提示