题面描述
思路
代码居然出奇的短
想都不想 LCT \operatorname{LCT} LCT
首先观察一下 o p t = 1 opt=1 opt=1,这不就是一个奇奇怪怪♂ 的 a c c e s s access access吗?想想 a c c e s s access access的操作,实际上就是实虚边的切换。
由于建树时是全部虚边,恰好就对应每个点的颜色不一样,那么每个点到根节点的权值就恰好是它的深度( d e p [ r t ] = 1 dep[rt]=1 dep[rt]=1)。
a c c e s s ( x ) access(x) access(x),将 x x x到 r t rt rt的路径成为实边,也就是颜色相同了。但考虑到虚边变成实边了,当前这个 x x x的右儿子需要发生改变,那么,原来的右儿子子树中到根节点的权值都需要 − 1 -1 −1,可是 LCT \operatorname{LCT} LCT不滋磁修改子树操作鸭。
那就大力 segment tree \operatorname{segment ~tree} segment tree线段树吧,但是怎么将其转化为一个区间呢?递归建树时维护一下dfs序即可。
o p t = 2 opt=2 opt=2时,大力求出 l c a ( x , y ) lca(x,y) lca(x,y),答案即为 c o l [ x ] + c o l [ y ] − c o l [ l c a ( x , y ) ] ∗ 2 + 1 col[x]+col[y]-col[lca(x,y)]*2+1 col[x]+col[y]−col[lca(x,y)]∗2+1
o p t = 3 opt=3 opt=3时,线段树乱搞。
AC code
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdlib>
#define gc getchar()
using namespace std;
const int N=1e5+10;
inline void qr(int &x)
{
x=0;char c=gc;int f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;}
x*=f;
}
void qw(int x)
{
if(x<0)x=-x,putchar('-');
if(x/10)qw(x/10);
putchar(x%10+48);
}
int st[N],ed[N],dep[N],tmp[N],fa[N][19],tp;
struct SGT{int l,r,mx,ad;}tr[N<<2];
#define pushdown(p) if(tr[p].ad){change(p<<1,tr[p].l,mid,tr[p].ad);change(p<<1|1,mid+1,tr[p].r,tr[p].ad);tr[p].ad=0;}
void update(int p){tr[p].mx=max(tr[p<<1].mx,tr[p<<1|1].mx);}
void build(int p,int l,int r)
{
if(l==r){tr[p].l=tr[p].r=l;tr[p].mx=tmp[l];return;}
int mid=(l+r)>>1;tr[p].l=l,tr[p].r=r;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
update(p);
}
void change(int p,int l,int r,int ad)
{
if(l<=tr[p].l&&r>=tr[p].r){tr[p].mx+=ad;tr[p].ad+=ad;return ;}
int mid=(tr[p].l+tr[p].r)>>1;
pushdown(p);
if(l<=mid)change(p<<1,l,r,ad);
if(r>mid)change(p<<1|1,l,r,ad);
update(p);
}
int query(int p,int l,int r)
{
if(l<=tr[p].l&&r>=tr[p].r)return tr[p].mx;
int mid=(tr[p].l+tr[p].r)>>1,val=0;pushdown(p);
if(l<=mid)val=max(val,query(p<<1,l,r));
if(r>mid)val=max(val,query(p<<1|1,l,r));
return val;
}
int query(int p,int x)
{
if(tr[p].l==tr[p].r)return tr[p].mx;
int mid=(tr[p].l+tr[p].r)>>1;pushdown(p);
if(x<=mid)return query(p<<1,x);
else return query(p<<1|1,x);
}
struct LCT{int f,son[2];}t[N];int rt;
inline bool nroot(int p){return t[t[p].f].son[0]==p||t[t[p].f].son[1]==p;}
void rotate(int p,int w)
{
int f=t[p].f,gf=t[f].f;
int r=t[p].son[w],R=f;t[R].son[w^1]=r;if(r)t[r].f=R;
r=p;R=gf;if(nroot(f))t[R].son[t[R].son[1]==f]=r;t[r].f=R;
r=f;R=p;t[R].son[w]=r;t[r].f=R;
}
void splay(int p)
{
while(nroot(p))
{
int f=t[p].f,gf=t[f].f;
if(!nroot(f))rotate(p,t[f].son[0]==p);
else
{
if(t[f].son[0]==p&&t[gf].son[0]==f)rotate(f,1),rotate(p,1);
else if(t[f].son[0]==p&&t[gf].son[1]==f)rotate(p,1),rotate(p,0);
else if(t[f].son[1]==p&&t[gf].son[0]==f)rotate(p,0),rotate(p,1);
else rotate(f,0),rotate(p,0);
}
}
}
int find(int x){while(t[x].son[0])x=t[x].son[0];return x;}
void access(int x)
{
for(int y=0,w;x;x=t[y=x].f)
{
splay(x);if(t[x].son[1])w=find(t[x].son[1]),change(1,st[w],ed[w],1);
if((t[x].son[1]=y))w=find(y),change(1,st[w],ed[w],-1);
}
}
struct edge{int y,next;}a[N<<1];int len,last[N];
void ins(int x,int y){a[++len]=(edge){y,last[x]};last[x]=len;}
void dfs(int x)
{
st[x]=++tp;t[x].f=fa[x][0];tmp[tp]=tmp[st[fa[x][0]]]+1;
for(int i=1;i<=18;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
for(int k=last[x],y;k;k=a[k].next)
{
if(t[x].f==(y=a[k].y))continue;
dep[y]=dep[x]+1;fa[y][0]=x;dfs(y);
}
ed[x]=tp;
}
int lca(int x,int y)
{
if(dep[x]>dep[y])swap(x,y);
for(int i=18;i>=0;i--)
if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
if(x==y)return x;
for(int i=18;i>=0;i--)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
int n,m;qr(n),qr(m);len=0;memset(last,0,sizeof(last));
for(int i=1,x,y;i<n;i++)qr(x),qr(y),ins(x,y),ins(y,x);
tp=0;dfs(1);build(1,1,n);
for(int i=1,opt,x,y;i<=m;i++)
{
qr(opt),qr(x);
if(opt==1)access(x);
else if(opt==2)qr(y),qw(query(1,st[x])+query(1,st[y])-2*query(1,st[lca(x,y)])+1),puts("");
else qw(query(1,st[x],ed[x])),puts("");
}
return 0;
}