题目描述
Description
在大量的试验之后,Mashmokh 设计了一个问题,然后,你的职责是解决它。
你得到一棵有n 个点的树T。每个点有一个独一无二的1 到n 之内的编号。树T 根的编号为1。对于树中每个点v,你会得到它儿子们按照特定顺序而给出的列表。你须处理在这棵树上的三种询问:
-
计算u 到v 的距离(最短路的边数);
-
给出v 和h,断开v 和他父亲的边,然后将它和它第h个祖先相连;更
正式的说法是,让我们记从v 到根的路径为x1, x2,… ; x_L(h <L),因此x1 = v 且x_L 为根;将v 与父亲(x2)的边断开,然后将它连上x_h+1;点v 必须添加到点x_h+1 的儿子列表的末尾;
- 在调用函数dfs(root) 产生的点序列里,找到其中最后的与根的距离为
k 的点。
函数dfs(v) 的伪代码如下:
// ls[v]: list of children of vertex v
// its i-th element is ls[v][i]
// its size is size(ls[v])
sequence result = empty sequence;
void dfs(vertex now)
{
add now to end of result;
for(int i = 1; i <= size(ls[v]); i = i + 1) //loop from i = 1 to i = size(ls[v])
dfs(ls[v][i]);
}
Input
输入的第一行包含两个用空格隔开的整数n,m(2 <= n <= 10^5; 1 <= m <= 10^5),T 的点数及需要处理的询问数。
接下来n 行的第i 行包含一个整数li(0 <= li <= n),第i 个点儿子的数目。
然后紧跟着的是li 个用空格隔开的整数,他们的第j 个为点i 的第j 个儿子的
编号。注意,这些点之间的顺序很重要。
接下来m行每行满足以下格式中的一个: “1 v u”,“2 v h”,或“3 k”。行中的第一个数为题目描述中需要处理的询问的类型。后面紧跟着的数字为询问的
参数。
保证所有询问都是正确的。例如,第二类询问中h 至少为2,最多为v 到根的距离。同样,在第三类询问给出时,有至少一个点与根的距离为k。
Output
对于每个第一类或第三类询问输出一行,包含询问的结果。
Sample Input
输入1:
4 9
1 2
1 3
1 4
0
1 1 4
2 4 2
1 3 4
3 1
3 2
2 3 2
1 1 2
3 1
3 2
输入2:
2 2
1 2
0
1 2 1
3 1
Sample Output
输出1:
3
2
2
4
1
3
4
输出2:
1
2
Data Constraint
对于30% 的数据,有n,m <= 1000。
题解
码农题(2/3)
貌似可以LCT+ETT维护?反正只用ETT也可以
其实也短不了多少
ETT全名Euler Tour Tree,即用平衡树维护欧拉序
为了维护深度,每个点上同时维护括号序的前缀和(即进栈+1,出栈-1)
(欧拉序维护的是点的顺序,所以把上图的根设为0)
对于欧拉序
01266244133550
括号序的和为
12343232121210
同时对于每个点,在ETT中的两个点(称之为左右括号)都维护在原树上的父亲
ETT能完成很多操作,以题目为例
①求x和y之间的最短路
其实就是deep[x]+deep[y]-2deep[lca]
问题变成了求lca的深度
可以发现,deep[lca]=[x的左括号~y的左括号]中最小的深度
分两种情况来看
1、如果x和y之间有祖先关系,那么找到的是x或y的左括号
2、如果x和y之间没有祖先关系,那么在走完一颗树时会退到lca的深度,即走到一个属于lca儿子的右括号(此时可以用记录的原树父亲来求出lca,但是本题没有必要)
②断开x,把x接为h级祖先的最后一个儿子
先找到[1~x的左括号]中最后一个深度为deep[x]-h的点
(由于欧拉序中深度是连续的一段区间,所以可以维护区间内深度最小/最大值来找)
同样分两种情况
1、找到的是一个左括号,那么就是x的h级祖先
2、找到的是一个右括号,那么x的h级祖先一定是这个点的原树父亲
(一个点在退栈时的深度=父亲的深度,该点深度=父亲深度+1,并且由1可得找到的点不会在h级祖先的子树外,所以找到的是h级祖先的一个儿子)
然后基本操作区间移动
③找到深度为k的最后一个点
类似②,找[1~n]中最后一个深度为k的点
分类
1、左括号,就是这个点
2、右括号,类似②,找到的实际上是最后一个深度为k+1的点,那么这个点的父亲就是最后一个深度为k的点
code
极其清真
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;
int a[100001][2];
int ls[100001];
int son[100001];
int bg[100001];
int ed[100001];
int tr[200003][7]; //0ls 1rs 2deep 3mnd 4mxd 5tag 6fa in real tree
int fa[200003];
int b[200003];
int N,n,Q,i,j,k,l,len,type,x,y,s;
void New(int x,int y)
{
++len;
a[len][0]=y;
a[len][1]=ls[x];
ls[x]=len;
}
void dfs(int Fa,int t,int d)
{
int i;
bg[t]=++j;
b[j]=t;
tr[j][2]=d;
tr[j][3]=d;
tr[j][4]=d;
tr[j][6]=Fa;
for (i=ls[t]; i; i=a[i][1])
dfs(t,a[i][0],d+1);
ed[t]=++j;
b[j]=t;
tr[j][2]=d-1;
tr[j][3]=d-1;
tr[j][4]=d-1;
tr[j][6]=Fa;
}
void down(int t)
{
if (tr[t][5])
{
if (tr[t][0])
tr[tr[t][0]][5]+=tr[t][5];
if (tr[t][1])
tr[tr[t][1]][5]+=tr[t][5];
tr[t][2]+=tr[t][5];
tr[t][3]+=tr[t][5];
tr[t][4]+=tr[t][5];
tr[t][5]=0;
}
}
void up(int t)
{
tr[t][3]=tr[t][2];
tr[t][4]=tr[t][2];
if (tr[t][0])
{
tr[t][3]=min(tr[t][3],tr[tr[t][0]][3]+tr[tr[t][0]][5]);
tr[t][4]=max(tr[t][4],tr[tr[t][0]][4]+tr[tr[t][0]][5]);
}
if (tr[t][1])
{
tr[t][3]=min(tr[t][3],tr[tr[t][1]][3]+tr[tr[t][1]][5]);
tr[t][4]=max(tr[t][4],tr[tr[t][1]][4]+tr[tr[t][1]][5]);
}
}
void rot(int t)
{
int Fa=fa[t],Fa2=fa[Fa];
int x=tr[Fa][1]==t,x2=tr[Fa2][1]==Fa;
int son=tr[t][x^1];
down(Fa);
down(t);
fa[t]=Fa2;
tr[Fa2][x2]=t;
fa[Fa]=t;
tr[t][x^1]=Fa;
fa[son]=Fa;
tr[Fa][x]=son;
up(Fa);
up(t);
}
void splay(int x,int t)
{
int Fa,Fa2;
while (fa[t]!=x)
{
Fa=fa[t];
if (fa[Fa]!=x)
{
Fa2=fa[Fa];
if (!((tr[Fa2][0]==Fa)^(tr[Fa][0]==t)))
rot(Fa),rot(t);
else
rot(t),rot(t);
}
else
rot(t);
}
}
void mt(int Fa,int l,int r)
{
int mid=(l+r)/2;
fa[mid]=Fa;
if (l==r)
return;
if (l+1==r)
{
tr[l][1]=r;
mt(l,r,r);
}
else
{
tr[mid][0]=(l+mid-1)/2;
mt(mid,l,mid-1);
tr[mid][1]=(mid+1+r)/2;
mt(mid,mid+1,r);
}
up(mid);
}
int main()
{
scanf("%d%d",&n,&Q);
N=n+n+2;
fo(i,1,n)
{
scanf("%d",&l);
fd(j,l,1)
scanf("%d",&son[j]);
fo(j,1,l)
New(i,son[j]);
}
j=1;
dfs(0,1,1);
++j;
bg[0]=1;
ed[0]=N;
mt(0,1,N);
for (;Q;--Q)
{
scanf("%d",&type);
switch (type)
{
case 1:{
scanf("%d%d",&x,&y);
if (x==y)
{
printf("0\n");
continue;
}
splay(0,bg[x]);
splay(bg[x],bg[y]);
down(bg[x]);
down(bg[y]);
s=min(tr[bg[x]][2],tr[bg[y]][2]);
if (tr[bg[x]][0]==bg[y])
{
if (tr[bg[y]][1])
s=min(s,tr[tr[bg[y]][1]][3]+tr[tr[bg[y]][1]][5]);
}
else
{
if (tr[bg[y]][0])
s=min(s,tr[tr[bg[y]][0]][3]+tr[tr[bg[y]][0]][5]);
}
printf("%d\n",tr[bg[x]][2]+tr[bg[y]][2]-s-s);
break;
}
case 2:{
scanf("%d%d",&x,&y);
splay(0,bg[x]);
down(bg[x]);
i=tr[bg[x]][0];
while (1)
{
down(i);
if (tr[i][1] && tr[tr[i][1]][3]+tr[tr[i][1]][5]<=tr[bg[x]][2]-y && tr[bg[x]][2]-y<=tr[tr[i][1]][4]+tr[tr[i][1]][5])
i=tr[i][1];
else
{
if (tr[i][2]==tr[bg[x]][2]-y)
break;
else
i=tr[i][0];
}
}
if (ed[b[i]]==i)
i=tr[i][6];
else
i=b[i];
splay(bg[x],ed[x]);
j=tr[ed[x]][1];
while (tr[j][0])
j=tr[j][0];
splay(ed[x],j);rot(j);rot(j);
k=tr[bg[x]][0];
while (tr[k][1])
k=tr[k][1];
splay(bg[x],k);rot(k);
// ---
down(j);
down(k);
tr[k][1]=0;
fa[bg[x]]=0;
up(k);
up(j);
// ---
splay(0,ed[i]);
j=tr[ed[i]][0];
while (tr[j][1])
j=tr[j][1];
splay(0,j);
tr[bg[x]][5]-=y-1;
down(bg[x]);
tr[ed[i]][0]=bg[x];
fa[bg[x]]=ed[i];
tr[bg[x]][6]=i;
tr[ed[x]][6]=i;
up(ed[i]);
up(j);
break;
}
case 3:{
scanf("%d",&x);
++x;
i=1;
while (fa[i])
i=fa[i];
while (1)
{
down(i);
if (tr[i][1] && tr[tr[i][1]][3]+tr[tr[i][1]][5]<=x && x<=tr[tr[i][1]][4]+tr[tr[i][1]][5])
i=tr[i][1];
else
{
if (tr[i][2]==x)
break;
else
i=tr[i][0];
}
}
if (ed[b[i]]==i)
printf("%d\n",tr[i][6]);
else
printf("%d\n",b[i]);
break;
}
}
}
}
参考资料
https://blog.csdn.net/jacajava/article/details/84475246
https://blog.csdn.net/zlttttt/article/details/78747431
https://blog.csdn.net/icefox_zhx/article/details/80691076
https://www.cnblogs.com/jefflyy/p/8352751.html
https://en.wikipedia.org/wiki/Euler_tour_technique