Gym 102218-Dynamic Network(倍增實現強制線上動態LCA)
題目連結:https://codeforces.com/gym/102218/problem/D
CSDN食用連結:https://blog.csdn.net/qq_43906000/article/details/107944082
Recently, a startup by a group of students has become more popular and there are plans for expanded offices. Bryan got an internship in this company and his task is to manage its internal network.
The company will acquire some computers over time. Initially, there is only a computer with id=1. When a new computer is added to the network, it's directly connected to a computer with id=p. The id of the new computer is the minimum positive integer that has not been used as the id of any other computer.
To determine the performance of the internal network, Bryan is required to answer several queries about what is the minimum number of computers through which a message must go if it is sent from computer with id=u to computer with id=v (start and end are also counted). Also, when a new computer is added, Bryan has to determine this number for a message sent from the new computer to the computer with id=1, since it is the most important computer. The computers have enough information to determine the optimum way to send a message to another computer.
Since there could be many computers in the network, Bryan needs your help to write a program that answers the queries. Note that the queries are encoded.
Input
The first line contains an integer \(Q (1≤Q≤2∗10^5)\) − the number of queries.
Each of the next Q line describes a query. It contains a integer \(t (t∈[1,2])\)
The queries will be encoded. Let last be the answer for the last query answered and let curr be the number of computers connected in the network so far. Initially \(last=0\) and \(curr=1\).
If \(t=1\), then one integer \(p′\) follows \((0≤p′≤2∗10^5)\). Set \(p=(p′+last)\%curr+1\). It means that a new computer is connected to the computer with \(id=p\). Increase the value of \(curr\) by one.
If \(t=2\), then two integers \(u′\) and \(v′\) follow \((0≤u′,v′≤2∗10^5)\). Set \(u=(u′+last)\%curr+1\) and \(v=(v′+last)\%curr+1\). It means that a message is sent from computer \(u\) to computer \(v\).
Output
If the query is of type 1, print the number of computers through which a message must go if it is sent from the new computer to computer with \(id=1\).
Otherwise print the amount of computers through which the message must go if it is sent from computer u to computer v.
Input
7
1 0
1 2
1 2
1 2
2 0 4
2 1 2
2 2 1
Output
2
2
3
3
4
2
3
Input
6
1 1
2 1 2
1 0
1 1
2 0 3
2 2 2
Output
2
2
2
2
3
1
Note
In the first sample, the new computers are connected as follows
Add new computer with id=2 and connect it to computer with id=1.
Add new computer with id=3 and connect it to computer with id=1.
Add new computer with id=4 and connect it to computer with id=2.
Add new computer with id=5 and connect it to computer with id=2.
And actual queries of the second type are:
u=4 and v=3
u=1 and v=2
u=5 and v=4
題目大意:剛開始的時候只有一個點是1,現在我有Q次操作,有兩種型別,當\(opt=1\)的時候,輸入\(p'\),可以得到\(p=(p'+last)\%curr+1\),那麼也就是有個新加入的節點會接在點\(p\)上面,其中\(last\)表示的是上一次的答案,\(curr\)表示的是當前點的數目,每次加入一個節點後\(curr+1\),同時輸出新加入的節點到點1中間會經過最少多少個節點,\(opt=2\)的時候輸入\(u',v'\),得到\(u=(u'+last)\%curr+1,v=(v'+last)\%curr+1\),輸出\(u\)到\(v\)直接最少會經過多少個節點。
emmm,只能說倍增nb。。。我們需要清楚的是倍增記錄的是父節點的資訊,而它也只是通過倍增取爬父鏈,那麼在新加入節點的父節點已經清楚的情況之下我們並不需要dfs,我們只需要更新一下其記錄的父節點的資訊就OK了,這也是為什麼它可以做到維護動態的LCA,其記錄和修改過程如下:
scanf ("%d",&p);
p=(p+last)%curr+1;
curr++;
depth[++cnt]=depth[p]+1;//記錄其深度
father[cnt][0]=p;
for (int i=1; i<=lg[depth[cnt]]+1; ++i)//記錄倍增的父節點
father[cnt][i]=father[father[cnt][i-1]][i-1];
last=depth[cnt];
那麼既然\(depth,father\)陣列都得出來了,其任意兩點間的LCA也就可以求了,畢竟LCA的爬鏈過程靠的也就是這兩個東西:
int LCA(int u,int v)
{
if (depth[u]<depth[v]) swap(u,v);
while (depth[u]!=depth[v]) u=father[u][lg[depth[u]-depth[v]]];
if (u==v) return u;
for (int i=lg[depth[u]]; i>=0; --i)
if (father[u][i]!=father[v][i]){
u=father[u][i];
v=father[v][i];
}
return father[u][0];
}
以下是AC程式碼:
#include <bits/stdc++.h>
using namespace std;
const int mac=2e5+10;
int depth[mac],father[mac][20];
int lg[mac];
int LCA(int u,int v)
{
if (depth[u]<depth[v]) swap(u,v);
while (depth[u]!=depth[v]) u=father[u][lg[depth[u]-depth[v]]];
if (u==v) return u;
for (int i=lg[depth[u]]; i>=0; --i)
if (father[u][i]!=father[v][i]){
u=father[u][i];
v=father[v][i];
}
return father[u][0];
}
int main(int argc, char const *argv[])
{
int q,opt;
scanf ("%d",&q);
int last=0,curr=1,cnt=1;
lg[0]=-1;
for (int i=1; i<mac-8; i++) lg[i]=lg[i>>1]+1;
depth[1]=1;
while (q--){
scanf ("%d",&opt);
int p,u,v;
if (opt==1){
scanf ("%d",&p);
p=(p+last)%curr+1; curr++;
depth[++cnt]=depth[p]+1;
father[cnt][0]=p;
for (int i=1; i<=lg[depth[cnt]]+1; ++i)
father[cnt][i]=father[father[cnt][i-1]][i-1];
last=depth[cnt];
}
else {
scanf ("%d%d",&u,&v);
u=(u+last)%curr+1; v=(v+last)%curr+1;
int id=LCA(u,v);
last=depth[u]+depth[v]-2*depth[id]+1;
}
printf("%d\n",last);
}
return 0;
}