題目描述
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