BZOJ3510 首都
题目描述
在X星球上有N个国家,每个国家占据着X星球的一座城市。由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的。
X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失,而B国的国土也将归A国管辖。A国国王为了加强统治,会在A国和B国之间修建一条公路,即选择原A国的某个城市和B国某个城市,修建一条连接这两座城市的公路。
同样为了便于统治自己的国家,国家的首都会选在某个使得其他城市到它距离之和最小的城市,这里的距离是指需要经过公路的条数,如果有多个这样的城市,编号最小的将成为首都。
现在告诉你发生在X星球的战事,需要你处理一些关于国家首都的信息,具体地,有如下3种信息需要处理:
1、A x y:表示某两个国家发生战乱,战胜国选择了x城市和y城市,在它们之间修建公路(保证其中城市一个在战胜国另一个在战败国)。
2、Q x:询问当前编号为x的城市所在国家的首都。
3、Xor:询问当前所有国家首都编号的异或和。
输入
第一行是整数N,M,表示城市数和需要处理的信息数。
接下来每行是一个信息,格式如题目描述(A、Q、Xor中的某一种)。
输出
输出包含若干行,为处理Q和Xor信息的结果。
样例输入
10 10
Xor
Q 1
A 10 1
A 1 4
Q 4
Q 10
A 7 6
Xor
Q 7
Xor
样例输出
11
1
1
1
2
6
2
题解
LCT维护子树信息
看了好几个小时的题解还是半懂不懂orz
先考虑连接两棵树
有两个性质
1.重心必在点数多的子树内
2.点数多的子树上重心移动的距离不超过点数少的子树的点数
然后就是连上之后,把这条路径拉出来,dfs一遍,枚举选哪一个点,然后判一下最优就好了
1 // luogu-judger-enable-o2 2 //minamoto 3 #include<iostream> 4 #include<cstdio> 5 #include<algorithm> 6 using std::swap; 7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 8 char buf[1<<21],*p1=buf,*p2=buf; 9 inline int read(){ 10 #define num ch-'0' 11 char ch;bool flag=0;int res; 12 while(!isdigit(ch=getc())) 13 (ch=='-')&&(flag=true); 14 for(res=num;isdigit(ch=getc());res=res*10+num); 15 (flag)&&(res=-res); 16 #undef num 17 return res; 18 } 19 char obuf[1<<24],*o=obuf; 20 inline void print(int x){ 21 if(x>9) print(x/10); 22 *o++=x%10+48; 23 } 24 const int N=100005; 25 int fa[N],st[N],sum[N],si[N],rev[N],ch[N][2],top,s; 26 inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} 27 inline void pushup(int x){sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+si[x]+1;} 28 inline void pushdown(int x){ 29 if(rev[x]){ 30 swap(ch[x][0],ch[x][1]); 31 rev[ch[x][0]]^=1,rev[ch[x][1]]^=1; 32 rev[x]=0; 33 } 34 } 35 void rotate(int x){ 36 int y=fa[x],z=fa[y],d=ch[y][1]==x; 37 if(!isroot(y)) ch[z][ch[z][1]==y]=x; 38 fa[x]=z,fa[y]=x,fa[ch[x][d^1]]=y,ch[y][d]=ch[x][d^1],ch[x][d^1]=y,pushup(y); 39 } 40 void down(int x){ 41 if(!isroot(x)) down(fa[x]); 42 pushdown(x); 43 } 44 void splay(int x){ 45 down(x); 46 for(int y=fa[x],z=fa[y];!isroot(x);y=fa[x],z=fa[y]){ 47 if(!isroot(y)) 48 ((ch[y][1]==x)^(ch[z][1]==y))?rotate(x):rotate(y); 49 rotate(x); 50 } 51 pushup(x); 52 } 53 void access(int x){ 54 for(int y=0;x;x=fa[y=x]) 55 splay(x),si[x]+=sum[ch[x][1]],si[x]-=sum[ch[x][1]=y],pushup(x); 56 } 57 void makeroot(int x){ 58 access(x),splay(x),rev[x]^=1; 59 } 60 int findroot(int x){ 61 access(x),splay(x),pushdown(x); 62 while(ch[x][0]) pushdown(x=ch[x][0]); 63 return x; 64 } 65 void split(int x,int y){ 66 makeroot(x),access(y),splay(y); 67 } 68 void link(int x,int y){ 69 split(x,y),fa[x]=y,si[y]+=sum[x],pushup(y); 70 } 71 void dfs(int x){ 72 pushdown(x); 73 if(ch[x][0]) dfs(ch[x][0]); 74 if(top>s) return; 75 st[++top]=x; 76 if(top>s) return; 77 if(ch[x][1]) dfs(ch[x][1]); 78 } 79 int main(){ 80 //freopen("testdata.in","r",stdin); 81 int n=read(),m=read(),res=0; 82 for(int i=1;i<=n;++i) sum[i]=1,res^=i; 83 while(m--){ 84 char c; 85 while((c=getc())!='Q'&&c!='A'&&c!='X'); 86 switch(c){ 87 case 'A':{ 88 int x=read(),y=read(); 89 int tx=findroot(x),ty=findroot(y),ts; 90 res^=tx^ty; 91 splay(tx),splay(ty); 92 if(sum[tx]>sum[ty]||(sum[tx]==sum[ty]&&x<y)) swap(x,y),swap(tx,ty); 93 s=sum[tx],ts=sum[tx]+sum[ty],link(x,y),access(x),splay(ty); 94 top=0,dfs(ty);int r=ty; 95 for(int i=1;i<=top;++i){ 96 splay(st[i]);int t=si[st[i]]+1+sum[ch[st[i]][1]]; 97 if(ts-t<t||(ts-t==t&&st[i]<=r)) r=st[i]; 98 else break; 99 /*枚举原重心到连接点之间的路径 100 t表示枚举到某一点时另一棵子树的大小 101 如果这棵子树大于另一边或那啥,更新答案 102 否则之前的答案最优,直接退出*/ 103 } 104 makeroot(r),res^=r; 105 break; 106 } 107 case 'Q':{ 108 int x=read(); 109 print(findroot(x)),*o++='\n'; 110 break; 111 } 112 case 'X':{ 113 print(res),*o++='\n'; 114 break; 115 } 116 } 117 } 118 fwrite(obuf,o-obuf,1,stdout); 119 return 0; 120 }
深深地明白自己的弱小