【JZOJ6096】森林
Description
我们定义对一棵树做一次变换的含义为:当以 1 号节点为根时,交换两个互相
不为祖先的点的子树;
一棵树的权值为对它进行至多一次变换能得到的最大直径长度;
初始时你只有一个节点 1,你需要执行 n-1 个操作,第 i 次操作会给出一个整
数 x,表示新加入第 i+1 号点,并与第 x 号点连一条边。每次操作后输出当前的树
的权值。
由于某些原因我们对数据进行了强制在线处理。
Solution
可以感受出来答案肯定是一条直径加直径上连出来的一条链的东西来得到。
维护这个东西相当容易。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
const int N=2e5+10;
int f[N][18],dep[N];
int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
fd(i,17,0) if(dep[f[u][i]]>=dep[v]) u=f[u][i];
if(u==v) return u;
fd(i,17,0) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][0];
}
int dis(int u,int v){
return dep[u]+dep[v]-2*dep[lca(u,v)];
}
int up(int x,int u,int v){
int lc=lca(u,v);
u=lca(x,u),v=lca(x,v);
if(dep[u]<dep[v]) swap(u,v);
if(dep[u]<dep[lc]) u=lc;
return u;
}
int zu,zv,p;
int ans;
void add(int x,int fr){
dep[x]=dep[fr]+1,f[x][0]=fr;
fo(i,1,17) f[x][i]=f[f[x][i-1]][i-1];
int t=dis(zu,zv),t1=dis(zu,x),t2=dis(zv,x),zm=up(x,zu,zv);
if(t1<t2) swap(zu,zv),swap(t1,t2);
if(t<t1){
p=dis(zv,zm)>dis(p,up(p,zu,x))?zv:p;
zv=x;
}
else p=dis(p,up(p,zu,zv))>dis(x,zm)?p:x;
ans=dis(zu,zv)+max(0,dis(p,up(p,zu,zv))-1);
}
int main()
{
freopen("forest.in","r",stdin);
freopen("forest.out","w",stdout);
int tp,n;
scanf("%d %d",&tp,&n);
dep[1]=1;
zu=zv=p=1;
fo(i,2,n){
int x;
scanf("%d",&x),x^=ans;
add(i,x);
printf("%d\n",ans);
}
}