Gym 102218D-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 type of query.

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;
}
posted @ 2020-08-11 20:53  lonely_wind  阅读(289)  评论(0编辑  收藏  举报