zoukankan      html  css  js  c++  java
  • 【洛谷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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Linux发行版 CentOS6.5 修改默认主机名
    《Linux就该这么学》培训笔记_ch06_存储结构与磁盘划分
    《Linux就该这么学》培训笔记_ch05_用户身份与文件权限
    《Linux就该这么学》培训笔记_ch04_Vim编辑器与Shell命令脚本
    《Linux就该这么学》培训笔记_ch03_管道符、重定向与环境变量
    《Linux就该这么学》培训笔记_ch02_一些必须掌握的Linux命令
    《Linux就该这么学》培训笔记_ch01_部署虚拟环境安装Linux系统
    《Linux就该这么学》培训笔记_ch00_认识Linux系统和红帽认证
    Swift笔记2
    request对象和response对象
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4299.html
Copyright © 2011-2022 走看看