zoukankan      html  css  js  c++  java
  • 【题解】 首都 LCT维护子树信息+二分 Luogu4299

    Legend

    初始有 (n (1 le n le 10^5)) 个孤立点,支持以下操作 (m (1 le m le 2 imes 10^5)) 次:

    • 连边 ((u,v)),保证图中无环;
    • 询问 (u) 所在树的重心;
    • 求所有树的中心异或和;

    当有多个重心时,取编号较小的那个。

    Editorial

    已知两棵树的重心,现在连一条边把它们连起来,有一个结论:

    Lemma1:新树的重心位于连接原本重心的路径上。

    证明显然,读者自证不难。

    那么现在的问题就是怎么快速求这个重心的位置?显然把这条路径 split 出来二分就行了。

    那么在 LCT 上怎么二分呢?

    LCT 是可以维护子树 size 的,所以可以直接做;

    你可能会问:不是只有根部的 size 信息才是真实的吗?我二分是在 splay 上二分的,肯定不能改变树的形态呀!

    事实上我们并不需要把当前这个点转到根,LCT 维护的是一条链,你可以在二分的过程中顺带求出对于当前二分点深度较深的一部分的子树大小。

    特别地,当出现两个重心的情况 ((2size_u = n)),还要检查二分到的重心的 parent 是否编号比较小。

    Code

    有一个叫做 search 的函数非常的重要。

    #include <bits/stdc++.h>
    
    #define debug(...) ;//fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    #define LL long long
    
    using namespace std;
    
    const int MX = 1e5 + 23;
    const LL MOD = 998244353;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    struct LCT{
    #define lch(x) ch[x][0]
    #define rch(x) ch[x][1]
    	int ch[MX][2] ,fa[MX] ,size[MX] ,Vsz[MX] ,rev[MX];
    	LCT(){for(int i = 1 ; i < MX ; ++i) size[i] = 1;}
    	int get(int x){return x == rch(fa[x]);}
    	int Nroot(int x){return get(x) || x == lch(fa[x]);}
    	void pushup(int x){size[x] = size[lch(x)] + size[rch(x)] + Vsz[x] + 1;}
    	void dorev(int x){swap(lch(x) ,rch(x)); rev[x] ^= 1;}
    	void pushdown(int x){if(rev[x]) dorev(lch(x)) ,dorev(rch(x)) ,rev[x] = 0;}
    	void rotate(int x){
    		int f = fa[x] ,gf = fa[f] ,which = get(x) ,W = ch[x][!which];
    		if(Nroot(f)) ch[gf][get(f)] = x;
    		ch[x][!which] = f ,ch[f][which] = W;
    		if(W) fa[W] = f;
    		fa[f] = x ,fa[x] = gf ,pushup(f);
    	}
    	int stk[MX] ,dep;
    	void splay(int x){
    		int f = x; stk[++dep] = f;
    		while(Nroot(f)) stk[++dep] = f = fa[f];
    		while(dep) pushdown(stk[dep--]);
    		while(Nroot(x)){
    			if(Nroot(f = fa[x])) rotate(get(x) == get(f) ? f : x);
    			rotate(x);
    		}pushup(x);
    	}
    	void access(int x){
    		for(int y = 0 ; x ; x = fa[y = x]){
    			splay(x);
    			Vsz[x] += size[rch(x)];
    			Vsz[x] -= size[rch(x) = y];
    			pushup(x);
    		}
    	}
    	void makeroot(int x){access(x) ,splay(x) ,dorev(x);}
    	void split(int x ,int y){makeroot(x) ,access(y) ,splay(x);}
    	void link(int x ,int y){
    		makeroot(x) ,access(y) ,splay(y);
    		fa[x] = y;
    		Vsz[y] += size[x];
    	}
    
    	int getpre(int x){
    		splay(x);
    		pushdown(x);
    		x = lch(x);
    		while(rch(x)) pushdown(x) ,x = rch(x);
    		return x;
    	}
    	int search(int x ,int allsz){
    		int ans = 0 ,up = 0 ,ans2 = 0;
    		int szans = INT_MAX ,szans2 = INT_MAX;
    		while(x){
    			pushdown(x);
    			int sznow = up + 1 + Vsz[x] + size[rch(x)];
    			int ok = 0;
    			if(sznow * 2 >= allsz){
    				ok = 1;
    				if(sznow < szans2){
    					ans2 = x;
    					szans2 = sznow;
    				}
    				if(szans2 < szans){
    					swap(szans2 ,szans);
    					swap(ans2 ,ans);
    				}
    			}
    			int szr = up + 1 + Vsz[rch(x)] + size[rch(rch(x))];
    			if(!lch(x) || (ok && rch(x))){
    				x = rch(x);
    			}
    			else{
    				up += 1 + size[rch(x)] + Vsz[x];
    				x = lch(x);
    			}
    		}
    		if(szans * 2 == allsz){
    			int x = getpre(ans);
    			if(x && x < ans) return x;
    		}
    		return ans;
    	}
    #undef lch
    #undef rch
    }T;
    
    int fa[MX] ,cap[MX] ,sz[MX];
    void init(){for(int i = 1 ; i < MX ; ++i) fa[i] = i ,sz[i] = 1;}
    int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
    void link(int x ,int y){
    	x = find(x) ,y = find(y);
    	if(x == y) return;
    	fa[x] = y ,sz[y] += sz[x];
    }
    
    void solve(){
    	int n = read() ,m = read();
    	init();
    
    	int con = 0;
    	for(int i = 1 ; i <= n ; ++i){
    		con ^= i;
    		cap[i] = i;
    	}
    
    	for(int i = 1 ; i <= m ; ++i){
    		char str[33];
    		scanf("%s" ,str);
    		if(str[0] == 'A'){
    			int x = read() ,y = read();
    
    			if(x == 5 && y == 10){
    				debug("ASDFDASDF");
    			}
    			con ^= cap[find(x)] ^ cap[find(y)];
    			T.link(x ,y);
    			T.split(cap[find(x)] ,cap[find(y)]);
    			int c = T.search(cap[find(x)] ,sz[find(x)] + sz[find(y)]);
    			link(x ,y);
    			cap[find(x)] = c;
    			debug("NEW CAP of(%d %d) = %d
    " ,x ,y ,c);
    			con ^= cap[find(x)];
    		}
    		if(str[0] == 'Q'){
    			printf("%d
    " ,cap[find(read())]);
    		}
    		if(str[0] == 'X'){
    			printf("%d
    " ,con);
    		}
    	}
    }
    
    int main(){
    	int T = 1;
    	for(int i = 1 ; i <= T ; ++i){
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    虚拟机安装Linux方案和操作系统启动流程
    CentOS7防止root密码被破解
    子网划分和VLAN
    Python之包的相关
    禁止复制文本的代码 HTML
    asp.net中Session过期设置方法
    CSS+DIV问题!DIV的最小高度问题!
    设置COOKIE过期时间的方法
    网站常见问题及解决方法(div/css)
    ASP.NET中如何删除最近打开的项目和文件的记录
  • 原文地址:https://www.cnblogs.com/imakf/p/13914750.html
Copyright © 2011-2022 走看看