zoukankan      html  css  js  c++  java
  • CF842E Nikita and game

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


    有一个显然的转化就是:有多少个点能作为直径端点 = 有多少点到直径中点距离等于直径长的一半。但直径中点可以在点上,也可以在某条边的中点。但两者本质是一样的,因为你可以往中点所在的边中间加一个点。所以下面只探讨在点上的情况。

    假设当前的直径中点为(rt),那么(rt)会把树分割为若干个子树,假设当前加入的点为(u)并且在以(v)为根的子树中(被(rt)分割出的子树)。设直径长度的一半为(len),那么我们考虑三种情况,一种是(u)(rt)的距离(<len),此时不需要做任何事;如果(=len),那么就多了一个能当端点的点;如果(>len),那么先前(v)子树中所有的答案点都不能作为端点了,那么答案减去这些点的数量,同时让(u)成为新的答案点,并且此时直径长度就(+1)了,(rt)会相应地移动半条边。

    所以我们需要做的就是查询并修改每个点子树内的答案点数量,很normal的dfn+线段树。

    复杂度(nlogn)

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define cn const
    #define gc getchar()
    #define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
    #define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
    #define go(u) for(rg int i=head[u];~i;i=e[i].nxt)
    using namespace std;
    typedef cn int cint;
    typedef long long LL;
    il int rd(){
    	rg int x(0),f(1); rg char c(gc);
    	while(c<'0'||'9'<c){if(c=='-')f=-1;c=gc;}
    	while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
    	return x*f;
    }
    cint maxn=3e5+10;
    int n,p[maxn],fa[maxn][19],dep[maxn],dfn[maxn],lst[maxn],tot;
    struct edge{int to,nxt;}e[maxn];
    int head[maxn],k;
    il void add(cint &u,cint &v){e[k]=(edge){v,head[u]},head[u]=k++;}
    void dfs(int u){
    	dfn[u]=++tot;
    	fp(i,1,18)fa[u][i]=fa[fa[u][i-1]][i-1];
    	go(u)dep[e[i].to]=dep[u]+1,dfs(e[i].to);
    	lst[u]=tot;
    }
    
    int cnt[maxn<<2],tg[maxn<<2];
    void ins(int d,int l,int r,cint &des){
    	if(l==r)return ++cnt[d],void();
    	if(tg[d])tg[d]=0,cnt[d<<1]=cnt[d<<1|1]=0,tg[d<<1]=tg[d<<1|1]=1;
    	int md=l+r>>1;
    	if(des<=md)ins(d<<1,l,md,des);
    	else ins(d<<1|1,md+1,r,des);
    	cnt[d]=cnt[d<<1]+cnt[d<<1|1];
    }
    void clr(int d,int l,int r,cint &dl,cint &dr){
    	if(dl<=l&&r<=dr)return tg[d]=1,cnt[d]=0,void();
    	if(tg[d])tg[d]=0,cnt[d<<1]=cnt[d<<1|1]=0,tg[d<<1]=tg[d<<1|1]=1;
    	int md=l+r>>1;
    	if(dl<=md)clr(d<<1,l,md,dl,dr);
    	if(dr>md)clr(d<<1|1,md+1,r,dl,dr);
    	cnt[d]=cnt[d<<1]+cnt[d<<1|1];
    }
    int qry(int d,int l,int r,cint &dl,cint &dr){
    	if(dl<=l&&r<=dr)return cnt[d];
    	if(tg[d])tg[d]=0,cnt[d<<1]=cnt[d<<1|1]=0,tg[d<<1]=tg[d<<1|1]=1;
    	int md=l+r>>1,ans=0;
    	if(dl<=md)ans=qry(d<<1,l,md,dl,dr);
    	if(dr>md)ans+=qry(d<<1|1,md+1,r,dl,dr);
    	return ans;
    }
    
    il int getlca(int u,int v){
    	if(dep[u]>dep[v])swap(u,v);
    	fb(i,18,0)if(dep[fa[v][i]]>=dep[u])v=fa[v][i];
    	if(u==v)return u;
    	fb(i,18,0)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
    	return fa[u][0];
    }
    
    int len,type,rt=1,rt1,rt2;
    il int slv1(cint &u){
    	rg int lca=getlca(u,rt),dis=dep[u]+dep[rt]-dep[lca]*2;
    	if(dis==len)ins(1,1,n,dfn[u]);
    	else if(dis>len){
    		if(dfn[rt]<=dfn[u]&&dfn[u]<=lst[rt]){
    			rg int son=u;
    			fb(i,18,0)if(dep[fa[son][i]]>dep[rt])son=fa[son][i];
    			clr(1,1,n,dfn[son],lst[son]),ins(1,1,n,dfn[u]);
    			type=1,rt1=rt,rt2=son;
    		}else{
    			if(dfn[rt]>1)clr(1,1,n,1,dfn[rt]-1);
    			if(lst[rt]<n)clr(1,1,n,lst[rt]+1,n);
    			ins(1,1,n,dfn[u]);
    			type=1,rt1=fa[rt][0],rt2=rt;
    		}
    	}
    	return cnt[1];
    }
    il int slv2(cint &u){//当然你也可以把边拆成点
    	rg int lca,dis;
    	if(dfn[rt2]<=dfn[u]&&dfn[u]<=lst[rt2]){
    		lca=getlca(u,rt2);
    		dis=dep[u]+dep[rt2]-dep[lca]*2;
    	}else{
    		lca=getlca(u,rt1);
    		dis=dep[u]+dep[rt1]-dep[lca]*2;
    	}
    	if(dis==len)ins(1,1,n,dfn[u]);
    	else if(dis>len){
    		if(dfn[rt2]<=dfn[u]&&dfn[u]<=lst[rt2]){
    			clr(1,1,n,dfn[rt2],lst[rt2]),ins(1,1,n,dfn[u]);
    			type=0,rt=rt2,++len;
    		}else{
    			if(dfn[rt2]>1)clr(1,1,n,1,dfn[rt2]-1);
    			if(lst[rt2]<n)clr(1,1,n,lst[rt2]+1,n);
    			ins(1,1,n,dfn[u]);
    			type=0,rt=rt1,++len;
    		}
    	}
    	return cnt[1];
    }
    int main(){
    	memset(head,-1,sizeof head);
    	n=rd()+1;
    	fp(i,2,n)p[i]=fa[i][0]=rd(),add(p[i],i);
    	dfs(1),ins(1,1,n,1);
    	fp(i,2,n){
    		if(type==0)printf("%d
    ",slv1(i));
    		else printf("%d
    ",slv2(i));
    	}
    	return 0;
    }
    
  • 相关阅读:
    Bundle 机制
    三次握手和四次挥手
    SparseArray
    ThreadLocal ——android消息机制handler在非主线程创建not called Looper.prepare() 错误的原因
    怎么去除重复代码
    ClassLoader
    android的四种线程池
    LeetCode#50 Pow(x, n)
    LeetCode#49 Anagrams
    LeetCode#48 Rotate Image
  • 原文地址:https://www.cnblogs.com/akura/p/14314569.html
Copyright © 2011-2022 走看看