洛谷4299首都(LCT維護動態重心+子樹資訊)
阿新 • • 發佈:2018-12-22
這個題目很有意思
QWQ
根據題目描述,我們可以知道,首都就是所謂的樹的重心,那麼我們假設每顆樹的重心都是\(root\)的話,對於每次詢問,我們只需要\(findroot(x)\)就可以。
那麼如何處理\(link\)操作呢QWQ
這裡是看了題解,我才知道是怎麼做的
大致的思想就是:
!啟發式合併!
首先,這裡需要注意樹的中心具有的兩個性質:
1。以這個點為根,那麼所有的子樹(不算整個樹自身)的大小都不超過整個樹大小的一半。
2.假設兩個聯通塊x和y進行合併,而且\(size(x)>size(y)\),那麼新的重心必然在連線原來兩棵樹重心的路徑上。
那麼我們對於一次\(link\)
void dfs(int x,int lim) { if (top>lim) { flag=true; return; } pushdown(x); if (ch[x][0]) dfs(ch[x][0],lim); if (flag) return; sta[++top]=x; if (flag) return; if (ch[x][1]) dfs(ch[x][1],lim); if (flag) return; } link(x,y); access(x); splay(ry); dfs(ry,ymh);
一定記得\(dfs\)的時候要\(pushdown\)!!!
同時dfs的時候,如果路徑上的點已經要比,較小的子樹的size要大,就可以直接\(return\),因為繼續下去一定沒有意義,就不可能會更新答案了。
然後把這條路徑統計出來之後,我們只需要從\(root_大\)開始,看看當前的節點的子樹大小*2,是不是大於總的\(size\),如果大於,就移動,不然就\(break\)
int r = ry; for (int i=1;i<=top;i++) { splay(sta[i]); int now = xv[sta[i]]+1+sum[ch[sta[i]][1]]; if (2*now>size || (2*now==size && sta[i]<=r)) r=sta[i]; else break; } makeroot(r); ans^=r;
大致就是這樣,然後對於整體的詢問,我們維護一個\(ans\)即可,每次合併之前先異或上兩個\(root\),然後最後再異或一下最後合併完的\(root\)
上程式碼
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 3e5+1e2;
int ch[maxn][3];
int fa[maxn],rev[maxn],st[maxn];
int n,m;
int sta[maxn];
int sum[maxn],xv[maxn];
int ans;
int son(int x)
{
if (ch[fa[x]][0]==x) return 0;
else return 1;
}
bool notroot(int x)
{
return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void update(int x)
{
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+xv[x]+1;
}
void reverse(int x)
{
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
void pushdown(int x)
{
if (rev[x])
{
if (ch[x][0]) reverse(ch[x][0]);
if (ch[x][1]) reverse(ch[x][1]);
rev[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y];
int b=son(x),c=son(y);
if (notroot(y)) ch[z][c]=x;
fa[x]=z;
ch[y][b]=ch[x][!b];
fa[ch[x][!b]]=y;
ch[x][!b]=y;
fa[y]=x;
update(y);
update(x);
}
void splay(int x)
{
int y=x,cnt=0;
st[++cnt]=y;
while (notroot(y)) y=fa[y],st[++cnt]=y;
while (cnt) pushdown(st[cnt--]);
while (notroot(x))
{
int y=fa[x],z=fa[y];
int b=son(x),c=son(y);
if (notroot(y))
{
if (b==c) rotate(y);
else rotate(x);
}
rotate(x);
}
update(x);
}
void access(int x)
{
for (int y=0;x;y=x,x=fa[x])
{
splay(x);
xv[x]+=sum[ch[x][1]]-sum[y];
ch[x][1]=y;
update(x);
}
}
void makeroot(int x)
{
access(x);
splay(x);
reverse(x);
}
int findroot(int x)
{
access(x);
splay(x);
while (ch[x][0])
{
pushdown(x);
x=ch[x][0];
}
return x;
}
void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y)
{
split(x,y);
if (findroot(y)!=x)
{
xv[y]+=sum[x];
fa[x]=y;
update(y);
}
}
int q;
bool flag=false;
int top;
void dfs(int x,int lim)
{
if (top>lim)
{
flag=true;
return;
}
pushdown(x);
if (ch[x][0]) dfs(ch[x][0],lim);
if (flag) return;
sta[++top]=x;
if (flag) return;
if (ch[x][1]) dfs(ch[x][1],lim);
if (flag) return;
}
int main()
{
n=read();q=read();
for (int i=1;i<=n;i++) sum[i]=1,ans^=i;
for (int i=1;i<=q;i++)
{
char s[10];
scanf("%s",s+1);
if (s[1]=='X')
{
cout<<ans<<"\n";
}
if (s[1]=='Q')
{
int x=read();
cout<<findroot(x)<<"\n";
}
if (s[1]=='A')
{
int x=read(),y=read();
flag=false;
top=0;
int rx=findroot(x);
splay(rx);
int ry=findroot(y);
splay(ry);
ans^=rx^ry;
if (sum[rx]>sum[ry] || (sum[rx]==sum[ry] && rx<ry)) swap(x,y),swap(rx,ry);
int ymh = sum[rx];int size = sum[rx]+sum[ry];
link(x,y);
access(x);
splay(ry);
dfs(ry,ymh);
int r = ry;
for (int i=1;i<=top;i++)
{
splay(sta[i]);
int now = xv[sta[i]]+1+sum[ch[sta[i]][1]];
if (2*now>size || (2*now==size && sta[i]<=r)) r=sta[i];
else break;
}
makeroot(r);
ans^=r;
}
}
return 0;
}