• 首都


    XIX.首都

    一句话题意:维护一棵森林,支持查询某棵树的重心以及所有树的重心的异或和。

    众所周知,重心有如下性质:将两棵树之间连一条边后,新树的重心在原两棵树重心的连线上。

    根据这一性质,我想了半天也没有想出来什么美妙的算法主要还是我splay没学好

    首先,这道题正解有两个,一是LCT+启发式合并(最暴力的断边重连那种),复杂度\(O(m\log^2 n)\);二是LCT+平衡树上二分,复杂度\(O(m\log n)\)

    第一种方法过于暴力,没有任何技术含量,但我仍然写不出来,这里就不具体介绍了。

    然后第二种方法,就是维护子树大小。每当我们连一条边后,将原两棵树重心连线split出来。然后在splay上二分,保证复杂度是一个\(\log\)

    局部代码:

    \(cen\)\(centroid\),重心之义;find(x)即为找到\(x\)的重心)

    inline void link(int u,int v){
    	makeroot(u),makeroot(v),t[u].fa=v,t[v].si+=t[u].sr,pushup(v);
    	u=find(u),v=find(v),split(u,v);
    	int ls=0,rs=0,mn=0x3f3f3f3f,x=v,lim=t[x].sr>>1;//ls&rs:the size of the subtree outside lson and rson
    	while(x){
    		pushdown(x);
    		int lsum=ls+t[lson].sr,rsum=rs+t[rson].sr;//the size of x's to sons
    		if(lsum<=lim&&rsum<=lim)mn=min(mn,x);
    		if(lsum<rsum)ls+=t[x].sr-t[rson].sr,x=rson;//add the size of the subtree of x minus the size of the subtree of rson
    		else rs+=t[x].sr-t[lson].sr,x=lson;
    	}
    	cen[u]=cen[v]=cen[mn]=mn,res^=u^v^mn;
    }
    

    总代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,cen[100100],res;
    int find(int x){return cen[x]==x?x:cen[x]=find(cen[x]);}
    #define lson t[x].ch[0]
    #define rson t[x].ch[1]
    struct LCT{
    	int fa,ch[2],si,sr;
    	bool rev;
    }t[100100];
    inline int identify(int x){
    	if(x==t[t[x].fa].ch[0])return 0;
    	if(x==t[t[x].fa].ch[1])return 1;
    	return -1;
    }
    inline void pushup(int x){
    	t[x].sr=t[x].si+1;
    	if(lson)t[x].sr+=t[lson].sr;
    	if(rson)t[x].sr+=t[rson].sr;
    }
    inline void REV(int x){t[x].rev^=1,swap(lson,rson);}
    inline void pushdown(int x){
    	if(!t[x].rev)return;
    	if(lson)REV(lson);
    	if(rson)REV(rson);
    	t[x].rev=0;
    }
    inline void rotate(int x){
    	register int y=t[x].fa,z=t[y].fa,dirx=identify(x),diry=identify(y),b=t[x].ch[!dirx];
    	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
    	if(b)t[b].fa=y;t[y].ch[dirx]=b;
    	t[y].fa=x,t[x].ch[!dirx]=y;
    	pushup(y),pushup(x);
    }
    inline void pushall(int x){if(identify(x)!=-1)pushall(t[x].fa);pushdown(x);}
    inline void splay(int x){for(pushall(x);identify(x)!=-1;rotate(x))if(identify(t[x].fa)!=-1)rotate(identify(x)==identify(t[x].fa)?t[x].fa:x);}
    inline void access(int x){for(register int y=0;x;x=t[y=x].fa)splay(x),t[x].si+=t[rson].sr-t[y].sr,rson=y,pushup(x);}
    inline void makeroot(int x){access(x),splay(x),REV(x);}
    inline int findroot(int x){access(x),splay(x),pushdown(x);while(lson)x=lson,pushdown(x);splay(x);return x;}
    inline void split(int x,int y){makeroot(x),access(y),splay(y);}
    inline void link(int u,int v){
    	makeroot(u),makeroot(v),t[u].fa=v,t[v].si+=t[u].sr,pushup(v);
    	u=find(u),v=find(v),split(u,v);
    	int ls=0,rs=0,mn=0x3f3f3f3f,x=v,lim=t[x].sr>>1;
    	while(x){
    		pushdown(x);
    		int lsum=ls+t[lson].sr,rsum=rs+t[rson].sr;
    		if(lsum<=lim&&rsum<=lim)mn=min(mn,x);
    		if(lsum<rsum)ls+=t[x].sr-t[rson].sr,x=rson;
    		else rs+=t[x].sr-t[lson].sr,x=lson;
    	}
    	cen[u]=cen[v]=cen[mn]=mn,res^=u^v^mn;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)cen[i]=i,res^=i;
    	for(int i=1,x,y;i<=m;i++){
    		char s[5];
    		scanf("%s",s);
    		if(s[0]=='A')scanf("%d%d",&x,&y),link(x,y);
    		if(s[0]=='Q')scanf("%d",&x),printf("%d\n",find(x));
    		if(s[0]=='X')printf("%d\n",res);
    	}
    	return 0;
    }
    

  • 相关阅读:
    多步操作产生错误,请检查每一步的状态值
    MediaPlayer 播放百度歌曲
    MusicPlayer
    wpf slider 控件模板
    c# 静态构造函数
    好吧,学了久c#,params都不知道怎么用,记录下
    C# 使用各种API
    WPF转义字符
    WPF 执行完一段动画后再关闭窗口
    WPF xml的绑定
  • 原文地址:https://www.cnblogs.com/Troverld/p/14602125.html
Copyright © 2020-2023  润新知