题目
正解
一个节点的值是它子树中的点到它的最大距离(即最大深度)。
对于所有的根,可以发现一个节点的子树内最大深度至多有两种。具体证明可以考虑两种最大深度从两个不同的方向伸出,父亲边至多会占据一个方向。
先将ilnil的做法:
对于每个节点记下这两个最大深度以及它们的方向。
以为根建树。假设当前的根为,可以发现只有到路径上的点的最大深度方向不是向下。
树链剖分,对于重链上的点,处理出不在重儿子方向上的最大深度;对于轻链,找到不是往方向的最大深度。
相应地在数据结构上维护即可。
题解的做法更加妙:找出直径的中点,可以证明直径的中点只有一个(如果直径的长度为奇数,则将中间那条边拆成两条边和一个虚点)。
对于每个点(根节点除外),可以发现它的两个最大深度分别是子树内最大深度,以及到其中一个端点的距离。也就是相当于一个往下一个往上。
当根节点变为其它点的时候,直径中点到它路径上的点的最大深度都向上指,其它的点的最大深度都向下指。
随便树剖一下,这样就可以更加方便地维护了。
具体怎样在数据结构上维护呢……
每个点有两种取值,一次换方向相当于这两个取值之间切换。
并且每个点有选或不选两种状态,选了才会对答案造成贡献。
一次换根操作需要切换取值,一次修改就是切换选或不选的状态。
于是这可以抽象成一个的矩阵,横座标表示选或不选,纵座标表示是当前使用的取值还是当前不用的取值。
维护的时候就是支持行翻转和列翻转的操作,然后维护总体的异或和。这个东西用线段树简单维护。
总时间复杂度
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
int input(){
char ch=getchar();
while (ch<'0' || ch>'9')
ch=getchar();
int x=0;
do{
x=x*10+ch-'0';
ch=getchar();
}
while ('0'<=ch && ch<='9');
return x;
}
int n,m;
struct EDGE{
int to;
EDGE *las;
} e[N*2];
int ne;
EDGE *last[N];
int L,R,len;
int rt,rt_;
int f[N],g[N];
int fa[N],dep[N],siz[N],hs[N],top[N],in[N],out[N],nowdfn,re[N];
void getdis(int x){
dep[x]=dep[fa[x]]+1;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa[x])
fa[ei->to]=x,getdis(ei->to);
}
void dfs1(int x){
siz[x]=1;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa[x]){
fa[ei->to]=x;
dep[ei->to]=dep[x]+1;
dfs1(ei->to);
siz[x]+=siz[ei->to];
if (siz[ei->to]>siz[hs[x]])
hs[x]=ei->to;
}
}
void dfs2(int x,int t){
in[x]=++nowdfn;
re[nowdfn]=x;
top[x]=t;
if (hs[x])
dfs2(hs[x],t);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa[x] && ei->to!=hs[x])
dfs2(ei->to,ei->to);
out[x]=nowdfn;
}
void init(int x){
f[x]=0;
g[x]=(in[x]<=in[L] && in[L]<=out[x]?dep[R]:dep[L])+dep[x]-(len&1);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa[x]){
init(ei->to);
f[x]=max(f[x],f[ei->to]+1);
}
}
struct Info{
int m[2][2];
void swap0(){swap(m[0][0],m[1][0]),swap(m[0][1],m[1][1]);}
void swap1(){swap(m[0][0],m[0][1]),swap(m[1][0],m[1][1]);}
};
Info operator^(Info &a,Info &b){
return {a.m[0][0]^b.m[0][0],a.m[0][1]^b.m[0][1],a.m[1][0]^b.m[1][0],a.m[1][1]^b.m[1][1]};
}
Info s[N*4];
int r0[N*4],r1[N*4];
int *ans;
void build(int k,int l,int r){
if (l==r){
int x=re[l];
s[k]={f[x],0,g[x],0};
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
s[k]=s[k<<1]^s[k<<1|1];
}
void pd(int k){
if (r0[k]){
s[k<<1].swap0(),s[k<<1|1].swap0();
r0[k<<1]^=1,r0[k<<1|1]^=1;
r0[k]=0;
}
if (r1[k]){
s[k<<1].swap1(),s[k<<1|1].swap1();
r1[k<<1]^=1,r1[k<<1|1]^=1;
r1[k]=0;
}
}
void change0(int k,int l,int r,int st,int en){
if (st<=l && r<=en){
s[k].swap0();
r0[k]^=1;
return;
}
pd(k);
int mid=l+r>>1;
if (st<=mid)
change0(k<<1,l,mid,st,en);
if (mid<en)
change0(k<<1|1,mid+1,r,st,en);
s[k]=s[k<<1]^s[k<<1|1];
}
void change1(int k,int l,int r,int st,int en){
if (st<=l && r<=en){
s[k].swap1();
r1[k]^=1;
return;
}
pd(k);
int mid=l+r>>1;
if (st<=mid)
change1(k<<1,l,mid,st,en);
if (mid<en)
change1(k<<1|1,mid+1,r,st,en);
s[k]=s[k<<1]^s[k<<1|1];
}
void mroot(int t){
int u=rt_,v=t;
for (;top[u]!=top[v];u=fa[top[u]]){
if (dep[top[u]]<dep[top[v]])
swap(u,v);
change0(1,1,n,in[top[u]],in[u]);
}
if (dep[u]<dep[v])
swap(u,v);
if (u!=v)
change0(1,1,n,in[hs[v]],in[u]);
rt_=t;
}
void workchain(int u,int v){
for (;top[u]!=top[v];u=fa[top[u]]){
if (dep[top[u]]<dep[top[v]])
swap(u,v);
change1(1,1,n,in[top[u]],in[u]);
}
if (dep[u]<dep[v])
swap(u,v);
change1(1,1,n,in[v],in[u]);
}
void worksubtree(int u){
if (u==rt_)
change1(1,1,n,1,n);
else if (in[u]<=in[rt_] && in[rt_]<=out[u]){
int x=hs[u];
if (!(in[x]<=in[rt_] && in[rt_]<=out[x])){
x=rt_;
for (;fa[top[x]]!=u;x=fa[top[x]]);
x=top[x];
}
if (1<=in[x]-1)
change1(1,1,n,1,in[x]-1);
if (out[x]+1<=n)
change1(1,1,n,out[x]+1,n);
}
else
change1(1,1,n,in[u],out[u]);
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=input(),m=input();
for (int i=1;i<n;++i){
int u=input(),v=input();
e[ne]={v,last[u]};
last[u]=e+ne++;
e[ne]={u,last[v]};
last[v]=e+ne++;
}
fa[1]=0,getdis(1);
L=1,R=1;
for (int i=2;i<=n;++i)
if (dep[i]>dep[L])
L=i;
fa[L]=0,getdis(L);
for (int i=2;i<=n;++i)
if (dep[i]>dep[R])
R=i;
len=dep[R]-1;
if (len&1){
int x=R,y;
for (int i=0;i<len>>1;++i)
x=fa[x];
y=fa[x];
rt=++n;
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to==y){
ei->to=rt;
break;
}
for (EDGE *ei=last[y];ei;ei=ei->las)
if (ei->to==x){
ei->to=rt;
break;
}
e[ne]={x,last[rt]};
last[rt]=e+ne++;
e[ne]={y,last[rt]};
last[rt]=e+ne++;
}
else{
rt=R;
for (int i=0;i<len>>1;++i)
rt=fa[rt];
}
fa[rt]=0,dep[rt]=0,dfs1(rt);
dfs2(rt,rt);
init(rt);
if (len&1)
f[rt]=0,g[rt]=0;
rt_=rt;
ans=&s[1].m[0][0];
build(1,1,n);
mroot(1);
while (m--){
int op;
scanf("%d",&op);
if (op==1){
int u,v,x;
scanf("%d%d%d",&u,&v,&x);
workchain(u,v);
mroot(x);
}
else{
int u,x;
scanf("%d%d",&u,&x);
worksubtree(u);
mroot(x);
}
printf("%d\n",*ans);
}
return 0;
}