• 首都


    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;
    }
    

  • 相关阅读:
    hdu 1290 献给杭电五十周年校庆的礼物 (DP)
    hdu 3123 GCC (数学)
    hdu 1207 汉诺塔II (DP)
    hdu 1267 下沙的沙子有几粒? (DP)
    hdu 1249 三角形 (DP)
    hdu 2132 An easy problem (递推)
    hdu 2139 Calculate the formula (递推)
    hdu 1284 钱币兑换问题 (DP)
    hdu 4151 The Special Number (DP)
    hdu 1143 Tri Tiling (DP)
  • 原文地址:https://www.cnblogs.com/Troverld/p/14602125.html
Copyright © 2020-2023  润新知