[CF842E]Nikita and game
[CF842E]Nikita and game
题目链接:
博客地址:
[CF842E]Nikita and game - skylee
题目大意:
一棵树初始只有一个编号为\(1\)的根结点。\(n(n\le3\times10^5)\)次操作,每次新增一个点作为\(p_i\)的子结点,询问更新后有多少点可以作为树直径的端点。
思路:
根据直径的一些性质,不难发现所有直径的相交部分一定是连续的一段。我们可以由此将所有可以作为端点的点分为三类:
- 在连续段左边的点
- 在连续段右边的点
- 无所谓左边和右边的点(此时相交部分变成了一个点)
设集合\(S_1\)中为\(1\)类点,集合\(S_2\)中为\(2\)类点,而\(3\)类点归为其中任意一个集合都可以。不难发现,从\(S_1,S_2\)中分别随机选取一个点,两个点之间的路径一定是树的直径。
每次新加入一个点后,分别与\(S_1\)和\(S_2\)中任意一个点求距离,记为\(d_1,d_2\)。记当前树的直径为\(maxd\),新加入的点为\(i\)。若\(\max(d_1,d_2)>maxd\),则直径可以被更新。若\(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;
}