bzoj 3510 首都
Description
在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:询问当前所有国家首都编号的异或和。
Input
第一行是整数N,M,表示城市数和需要处理的信息数。
接下来每行是一个信息,格式如题目描述(A、Q、Xor中的某一种)。
Output
输出包含若干行,为处理Q和Xor信息的结果。
Sample Input
Xor
Q 1
A 10 1
A 1 4
Q 4
Q 10
A 7 6
Xor
Q 7
Xor
Sample Output
1
1
1
2
6
2
HINT
对于100%的数据,2<=N<=100000,1<=M<=200000。
LCT维护子树信息(+启发式合并)
这道题也真是强啊,分析了一会码了一会,结果却因为一个傻x错误坑了我一个多小时~
首先,在link时,要把点数少的连接到点数多的上(这其实不应该叫做启发式合并吧)
这样有什么好处?它有两个重要的性质:
1.合并后的重心一定在点数多的树之内,且在连接点到原重心的链上(因为如果在点数少的树之内,重心最后一段的移动路径一定对答案的贡献恒为负,一定不是最优解;而偏移方向不为到连接点方向的话对答案贡献也一定为负)
2.合并后的重心与原重心距离一定不超过点数少的树的点数(假设一个一个插入,偏移距离一定不超过1)
这就可以看出这样做的优势:性质1限定了重心移动的方向,性质2限定了重心移动的距离。
我们考虑:重心发生改变,把它移动的路径分为每次一条边的段,那么每一段对于答案的贡献一定是递减的,直到某一段对答案贡献为负则停止。
那么我们就可以模拟这个过程,将重心设为树根,每次把重心可能的移动路径拿出来,一个一个判断并处理。
嘴上说真简单
实际上,要动态维护子树大小,需要使用LCT维护子树信息。而在LCT中取出重心的移动路径并不是特别容易,需要求出Splay Tree的中序遍历,就要dfs整棵Splay Tree,并在超过范围时停止。
由于重心是固定的,因此将x合并到y上时不能makeroot(y),只能access(y),splay(y)
更复杂的问题是题目不是使用spj,而是强制要求有多个重心时需要选择编号较小的。所以还应该判断编号的影响。
最重要的是,findroot和dfs时都需要pushdown!一开始我在findroot时想起来了,结果到dfs时又忘了,因为这个sb错误zz了一个小时真是气。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define rep(i,a,b) for(R i=a;i<=b;i++) 5 #define Rep(i,a,b) for(R i=a;i>=b;i--) 6 #define ms(i,a) memset(a,i,sizeof(a)) 7 #define gc() getchar() 8 #define LL long long 9 #define I inline int 10 #define pa fa[x] 11 #define lc ch[x][0] 12 #define rc ch[x][1] 13 template<class T>void read(T &x){ 14 x=0; char c=0; 15 while (!isdigit(c)) c=gc(); 16 while (isdigit(c)) x=x*10+(c^48),c=gc(); 17 } 18 int const N=100000+3; 19 int const M=200000+3; 20 int n,m,top,st[N],ans,tp,sk[N]; 21 int fa[N],ch[N][2],r[N],sum[N],sz[N]; 22 I get(int x){return ch[pa][1]==x;} 23 I isr(int x){return ch[pa][0]!=x && ch[pa][1]!=x; } 24 I update(int x){sum[x]=sum[lc]+sum[rc]+sz[x]+1;} 25 I con(int x,int y,int z){fa[x]=y;ch[y][z]=x;} 26 I rotate(int x){ 27 int f=fa[x],g=fa[f],c=get(x),cc=get(f); 28 if (!isr(f)) ch[g][cc]=x; fa[x]=g; 29 con(ch[x][c^1],f,c);con(f,x,c^1); 30 update(f); update(x); 31 } 32 I pushd(int x){ 33 if(!r[x]) return 0; 34 swap(ch[lc][0],ch[lc][1]); 35 swap(ch[rc][0],ch[rc][1]); 36 r[lc]^=1; r[rc]^=1; r[x]=0; 37 } 38 I splay(int x){ 39 top=0; int k=x; while (!isr(k)) st[++top]=k,k=fa[k]; st[++top]=k; 40 while (top) pushd(st[top--]); 41 for(;!isr(x);rotate(x)) if(!isr(pa)) rotate(get(x)==get(pa)? pa :x); 42 } 43 I access(int x){ 44 for(int y=0; x; y=x,x=pa){ 45 splay(x); 46 sz[x]+=sum[rc]-sum[y]; 47 rc=y,update(x); 48 } 49 } 50 I make(int x){ 51 access(x); splay(x); r[x]^=1; swap(lc,rc); 52 } 53 I find(int x){ 54 access(x); splay(x); 55 while (lc) x=lc; 56 return x; 57 } 58 I split(int x,int y){ 59 make(x); access(y); splay(y); 60 } 61 I link(int x,int y){ 62 make(x); fa[x]=y; sz[y]+=sum[x]; update(y); 63 } 64 void dfs(int x,int s){ 65 if(tp>s) return ; 66 pushd(x); 67 if(lc) dfs(lc,s); 68 sk[++tp]=x; 69 if(rc) dfs(rc,s); 70 } 71 int main(){ 72 read(n); read(m); 73 rep(i,1,n) ans^=i,sum[i]=1; 74 while (m--){ 75 char s[10]; 76 scanf("%s",s); 77 if(s[0]=='X') printf("%d\n",ans); 78 if(s[0]=='A'){ 79 int x,y; read(x); read(y); 80 int tx=find(x),ty=find(y); 81 ans^=tx; ans^=ty; 82 splay(tx); splay(ty); 83 if(sum[tx]>sum[ty] || sum[tx]==sum[ty] && x<y) swap(x,y),swap(tx,ty); 84 int s=sum[tx], ts=sum[tx]+sum[ty]; 85 link(x,y);access(x); splay(ty); 86 tp=0,dfs(ty,s); int r=ty; 87 rep(i,1,tp){ 88 splay(sk[i]); int t=sz[sk[i]]+1+sum[ch[sk[i]][1]]; 89 if(2*t>ts || 2*t==ts && r>=sk[i]) r=sk[i]; 90 } 91 make(r); ans^=r; 92 } 93 if(s[0]=='Q') { 94 int x; read(x); 95 printf("%d\n",find(x)); 96 } 97 } 98 }