• [CF842E]Nikita and game


    [CF842E]Nikita and game

    题目链接:

    CF842E

    博客地址:

    [CF842E]Nikita and game - skylee

    题目大意:

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

    思路:

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

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

    设集合(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(nlog n))。而那些用std::set的虽然同样也是(mathcal O(nlog 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
    ",s1.size()+s2.size());
    	}
    	return 0;
    }
    
  • 相关阅读:
    无线安全课堂:手把手教会你搭建伪AP接入点
    转载——开阔自己的视野,勇敢的接触新知识
    关于系统架构的一些总结
    MessageBox.Show()如何换行
    不患寡而患不均
    由CHAR(2)引发的BUG
    DataRow.RowState 属性
    C# 使用TimeSpan计算两个时间差
    利用反射调出其他项目的界面
    DB2 中将date类型的转换成timestamp
  • 原文地址:https://www.cnblogs.com/skylee03/p/9081983.html
Copyright © 2020-2023  润新知