• 【洛谷4299】首都(LCT维护树的重心)


    点此看题面

    • 初始有(n)个点,(q)次操作,分为三种:在未连通的两点(x,y)间连一条边;询问(x)所在树的重心;询问所有树的重心的异或和。
    • (nle10^5,qle2 imes10^5)

    (LCT)维护树的重心

    首先必须知道一个重要结论:两棵树合并之后,新的重心必然在原先两个重心间的路径上

    我们用(LCT)维护合并操作,那么想要找到新的重心,就是利用(LCT)把这条链抠出来,然后在(Splay)上二分。

    此次又利用到重心的另一个性质,这个就比较常见了:重心的各子树大小都小于等于整棵树的一半。

    (LCT)维护子树信息应该也算个经典问题了,对每个点维护个虚儿子总大小即可。

    而要询问所有树的重心的异或和,因为一次操作只会减少两个重心,新增一个重心,直接开个变量记录异或和就好了。

    代码:(O(nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 100000
    using namespace std;
    int n,f[N+5],g[N+5];I int F(CI x) {return f[x]^x?f[x]=F(f[x]):x;}//并查集指向重心
    class LinkCutTree
    {
    	private:
    		#define PU(x) (O[x].Sz=O[O[x].S[0]].Sz+O[O[x].S[1]].Sz+O[x].ISz+1)
    		#define IR(x) (O[O[x].F].S[0]^x&&O[O[x].F].S[1]^x)
    		#define Wh(x) (O[O[x].F].S[1]==x)
    		#define Co(x,y,d) (O[O[x].F=y].S[d]=x)
    		#define PD(x) (O[x].R&&(Re(O[x].S[0]),Re(O[x].S[1]),O[x].R=0))
    		#define Re(x) (swap(O[x].S[0],O[x].S[1]),O[x].R^=1)
    		#define MR(x) (Ac(x),S(x),Re(x))
    		struct node {int Sz,ISz,R,F,S[2];}O[N+5];
    		I void Ro(RI x) {RI f=O[x].F,p=O[f].F,d=Wh(x);
    			!IR(f)&&(O[p].S[Wh(f)]=x),O[x].F=p,Co(O[x].S[d^1],f,d),Co(f,x,d^1),PU(f);}
    		int St[N+5];I void S(RI x) {RI f=x,T=0;W(St[++T]=f,!IR(f)) f=O[f].F;
    			W(T) PD(St[T]),--T;W(!IR(x)) f=O[x].F,!IR(f)&&(Ro(Wh(f)^Wh(x)?x:f),0),Ro(x);PU(x);}
    		I void Ac(RI x) {for(RI y=0;x;x=O[y=x].F) S(x),O[x].ISz+=O[O[x].S[1]].Sz-O[y].Sz,O[x].S[1]=y,PU(x);}
    	public:
    		I void Init() {for(RI i=1;i<=n;++i) O[i].Sz=1;}
    		I void Link(CI x,CI y) {MR(x),Ac(y),S(y),O[O[x].F=y].ISz+=O[x].Sz,PU(y);}
    		I int Get(CI x,CI y,CI s)//在x,y路径上二分新的重心
    		{
    			MR(x),Ac(y),S(y);RI k=y,p=n,ls,rs,L=0,R=0;W(k)
    			{
    				PD(k),ls=O[O[k].S[0]].Sz+L,rs=O[O[k].S[1]].Sz+R,ls<=(s>>1)&&rs<=(s>>1)&&(p=min(p,k));//判断当前点是否为重心
    				ls>rs?(R+=O[k].Sz,k=O[k].S[0],R-=O[k].Sz):(L+=O[k].Sz,k=O[k].S[1],L-=O[k].Sz);//进入较大的子树
    			}return S(p),p;
    		}
    }LCT;
    int main()
    {
    	RI Qt,i,t=0;for(scanf("%d%d",&n,&Qt),LCT.Init(),i=1;i<=n;++i) t^=(f[i]=i),g[i]=1;//初始化
    	RI x,y,fx,fy,fz;char op[5];W(Qt--) switch(scanf("%s",op),op[0])
    	{
    		case 'A':scanf("%d%d",&x,&y),t^=(fx=F(x))^(fy=F(y)),LCT.Link(x,y);//消去原本重心的贡献;连边
    			fz=LCT.Get(fx,fy,g[fx]+g[fy]),t^=(f[fx]=f[fy]=f[fz]=fz),g[fz]=g[fx]+g[fy];break;//求出新的重心,修改并查集
    		case 'Q':scanf("%d",&x),printf("%d
    ",F(x));break;case 'X':printf("%d
    ",t);break;//询问
    	}return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    java堆
    本地方法栈
    java虚拟机栈
    Java 程序计数器
    面向对象 基本概念 复习
    if __name__=='__main__'
    偏函数与模块
    可变参数与关键字参数(复习材料)
    匿名函数
    闭包
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4299.html
Copyright © 2020-2023  润新知