【bzoj4764】弹飞大爷 LCT

题目描述

给出一张有向图,每个点最多只有一条出边。多次修改一个点的出边,并询问从某个点开始经过多少个点能够到达出度为0的点(走不到则输出-1)

输入

第一行为两个整数N和M,代表序列长度和操作次数。
第二行为N个整数,代表初始的小伙伴序列。
接下来有M行,每行代表一个操作。
如果这一行的第一个数是1,代表该操作是一个询问操作,接下来一个数X,代表询问此时大爷从X处,经过几次弹
起会摔在地上。如果永远不会摔在地上,请输出-1。
如果这一行的第一个数是2,代表该操作是一个更改操作,接下来两个数X,Y,代表将序列的第X项改为Y。
N,M <= 200000  |Ai| < N

输出

对于每次询问操作,输出弹起次数或-1。

样例输入

3 19
1 1 1
1 1
1 2
1 3
2 1 2
1 1
1 2
1 3
2 3 -1
1 1
1 2
1 3
2 2 233
1 1
1 2
1 3
2 2 -233
1 1
1 2
1 3

样例输出

3
2
1
2
2
1
-1
-1
-1
3
1
2
3
1
2


题解

LCT

容易发现每个“连通块”都是有根树或基环内向树。

对于有根树可以直接使用LCT维护。

对于基环内向树可以看作是有根树加上一条从根节点到某个点的边。我们使用LCT维护这个有根树,并在根节点出记录一下这条边到的点是哪个。

对于link操作如果是普通边则直接连,如果是这一条特殊的边则记录下来。

对于cut操作如果割断的是特殊的边则直接更改记录的信息,否则割断以后还要判断这条边加上后是否形成环,如果不形成环的话还要把这个记录的边当做一般的树边link上。

另外需要注意的是:有根树不能makeroot,但是仍然需要access&splay完成操作。

具体细节还是看代码吧。

时间复杂度 $O(LCT·n\log n)$ 

本题TLE不一定是因为常数大,很有可能是写挂了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
using namespace std;
int a[N] , fa[N] , c[2][N] , si[N] , pos[N];
inline void pushup(int x)
{
	si[x] = si[c[0][x]] + si[c[1][x]] + 1;
}
inline bool isroot(int x)
{
	return x != c[0][fa[x]] && x != c[1][fa[x]];
}
inline void rotate(int x)
{
	int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1;
	if(!isroot(y)) c[c[1][z] == y][z] = x;
	fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y;
	pushup(y) , pushup(x);
}
inline void splay(int x)
{
	int y , z;
	while(!isroot(x))
	{
		y = fa[x] , z = fa[y];
		if(!isroot(y))
		{
			if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
}
inline void access(int x)
{
	int t = 0;
	while(x) splay(x) , c[1][x] = t , pushup(x) , t = x , x = fa[x];
}
inline int find(int x)
{
	access(x) , splay(x);
	while(c[0][x]) x = c[0][x];
	splay(x);
	return x;
}
inline void link(int x , int y)
{
	if(find(y) == x) pos[x] = y;
	else access(x) , splay(x) , fa[x] = y;
}
inline void cut(int x , int y)
{
	if(pos[x] == y) pos[x] = 0;
	else
	{
		int t = find(x);
		access(x) , splay(x) , fa[c[0][x]] = 0 , c[0][x] = 0 , pushup(x);
		if(pos[t] && find(pos[t]) != t) link(t , pos[t]) , pos[t] = 0;
	}
}
int main()
{
	int n , m , i , opt , x , y;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , si[i] = 1;
	for(i = 1 ; i <= n ; i ++ )
		if(i + a[i] >= 1 && i + a[i] <= n)
			link(i , i + a[i]);
	while(m -- )
	{
		scanf("%d%d" , &opt , &x);
		if(opt == 1)
		{
			access(x) , y = find(x);
			if(pos[y]) puts("-1");
			else splay(x) , printf("%d\n" , si[x]);
		}
		else
		{
			scanf("%d" , &y);
			if(x + a[x] >= 1 && x + a[x] <= n) cut(x , x + a[x]);
			if(x + y >= 1 && x + y <= n) link(x , x + y);
			a[x] = y;
		}
	}
	return 0;
}

 

posted @ 2017-11-23 08:31  GXZlegend  阅读(575)  评论(0编辑  收藏  举报