题目大意
有一棵树,一开始只有一个节点
1
,权值为
接下来会有
q
个操作,操作有以下三种:
∙
将节点
x
的权值修改为
∙
询问这棵树的带权重心的编号,如果有两个,那么选择离
1
号节点最近的那个
本题强制在线。
题目分析
既然有动态改变树的形态,那么考虑使用LCT。
怎么在LCT上找带权重心呢?既然要求是离
1
号节点最近的一个,我们考虑从
至于修改操作,我直接
access
这个点然后在
splay
上打上tag就好了。
注意虚实边切换的细节要想好,不然很容易错。
时间复杂度
O(nlog2n)
(虚实边切换是
O(logn)
次的,每一次都要更新
set
)。
代码实现
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <stack>
#include <set>
using namespace std;
typedef long long LL;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
int buf[30];
void write(int x)
{
if (x<0) putchar('-'),x=-x;
for (;x;x/=10) buf[++buf[0]]=x%10;
if (!buf[0]) buf[++buf[0]]=0;
for (;buf[0];putchar('0'+buf[buf[0]--]));
}
const int N=150050;
set<LL> heap[N];
LL all;
int n,q,ans;
int p[N];
bool is_online;
namespace link_cut_tree
{
int fa[N],par[N],size[N],LEFT[N];
LL v[N],mx[N],tag[N];
int son[N][2];
stack<int> st;
bool side(int x){return son[fa[x]][1]==x;}
void update(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1,LEFT[x]=son[x][0]?LEFT[son[x][0]]:x,mx[x]=max(v[x],max(mx[son[x][0]],mx[son[x][1]]));}
void ADD(int x,LL delta){v[x]+=delta,mx[x]+=delta,tag[x]+=delta;}
void clear(int x)
{
if (tag[x])
{
if (son[x][0]) ADD(son[x][0],tag[x]);
if (son[x][1]) ADD(son[x][1],tag[x]);
tag[x]=0;
}
}
void pushdown(int x,int y)
{
for (;x!=y;st.push(x),x=fa[x]);
for (;!st.empty();clear(st.top()),st.pop());
}
void rotate(int x)
{
int y=fa[x];bool s=side(x);
if (fa[y]) son[fa[y]][side(y)]=x;
if (son[x][s^1]) fa[son[x][s^1]]=y;
son[y][s]=son[x][s^1],son[x][s^1]=y;
fa[x]=fa[y],fa[y]=x;
if (par[y]) par[x]=par[y],par[y]=0;
update(y),update(x);
}
void splay(int x,int y)
{
for (pushdown(x,y);fa[x]!=y;rotate(x))
if (fa[fa[x]]!=y)
if (side(x)==side(fa[x])) rotate(fa[x]);
else rotate(x);
}
int search(int x)
{
clear(x);
if (mx[son[x][1]]<<1>all) return search(son[x][1]);
return v[x]<<1>all?x:search(son[x][0]);
}
int getlight(int x,int y){return splay(y=LEFT[y],x),heap[x].insert((v[y]<<20)+y),y;}
int getheavy(int x,int y){return splay(y=LEFT[y],0),heap[x].erase((v[y]<<20)+y),y;}
int access(int x)
{
int nxt=0;
for (;x;update(nxt=x),x=par[x])
{
splay(x,0);
if (son[x][1]) son[x][1]=getlight(x,son[x][1]),par[son[x][1]]=x,fa[son[x][1]]=0;
if (nxt) nxt=getheavy(x,nxt),par[nxt]=0,fa[nxt]=x;
son[x][1]=nxt;
}
return nxt;
}
void modify(int x,int delta){ADD(access(x),delta),all+=delta;}
void addcity(int x,int y){p[++n]=y,par[n]=x,update(n),modify(n,y);}
int core()
{
int ret=0;
for (int x=1,y;!ret;x=y)
{
splay(x,0),x=search(x);
LL get=heap[x].empty()?0:(*heap[x].rbegin());
if ((get>>20)<<1<=all) ret=x;
y=get&((1<<20)-1);
}
return access(ret),ret;
}
};
int main()
{
using namespace link_cut_tree;
freopen("jueban.in","r",stdin),freopen("jueban.out","w",stdout);
for (is_online=1,q=read(),addcity(0,read());q--;)
{
int tp=read(),x,y;
switch (tp)
{
case 1:x=read()^(ans*is_online),y=read()^(ans*is_online),addcity(x,y);/*printf("1 %d %d\n",x,y);*/break;
case 2:x=read()^(ans*is_online),y=read()^(ans*is_online),modify(x,y-p[x]),p[x]=y;/*printf("2 %d %d\n",x,y);*/break;
default:write(ans=core()),putchar('\n');
}
}
fclose(stdin),fclose(stdout);
return 0;
}