【HNOI 2010】弹飞绵羊

【题目】

传送门

题目描述:

Lostmonkey 发明了一种超级反弹装置。为了在绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey 在地上沿一条直线摆放 n n n 个反弹装置,并按从前往后的方式将反弹装置依次编号为 0 0 0 n − 1 n-1 n1,对 0 ≤ i ≤ n − 1 0≤i≤n-1 0in1,为第 i i i 个反弹装置设定了初始弹力系数 k i k_i ki,当绵羊落到第 i i i 个反弹装置上时,它将被往后弹出 k i k_i ki 步,即落到第 i + k i i+k_i i+ki 个反弹装置上,若不存在第 i + k i i+k_i i+ki 个反弹装置,则绵羊被弹飞。绵羊想知道:从第 i i i 个反弹装置开始,它被弹出几次(含被弹飞的那次)后会被弹飞。为使游戏更有趣,Lostmonkey 还可以修改某个反弹装置的弹力系数,但任何时候弹力系数均为正整数。

输入格式:

输入第一行是一个整数 n n n ,表示地上摆放 n n n 个反弹装置。

输入第二行是用空格隔开的 n n n 个正整数 k 0 k_0 k0 k 1 k_1 k1 ⋯ ⋯ k n − 1 k_{n-1} kn1,分别表示 n n n 个反弹装置的初始弹力系数。

输入第三行是一个正整数 m m m,表示后面还有 m m m 行输入数据。

接下来的 m m m 行,每行至少有用空格隔开的两个整数 i i i j j j ,若 i = 1 i=1 i=1,则你要输出从第 j j j 个反弹装置开始,被弹出几次后会被弹飞;若 i = 2 i=2 i=2,则该行有用空格隔开的三个整数 i i i j j j k k k,表示第 j j j 个反弹装置的弹力系数被修改为 k k k

输出格式:

输出包含的行数等于输入文件最后 m m m 行中 i = 1 i=1 i=1 的行数。第 h h h 行输出一个整数,表示对输入中给出的第 h h h 个求弹出次数的问题,基于 n n n 个反弹装置当时的弹力系数,求出的弹出次数。

样例数据:

输入
4
1 2 1 1
3
1 1
2 1 1
1 1

输出
2
3

备注:

【数据范围】

20 % 20\% 20% 的数据满足: n , m ≤ 10000 n,m≤10000 n,m10000
100 % 100\% 100% 的数据满足: n ≤ 200000 n≤200000 n200000 m ≤ 100000 m≤100000 m100000


【分析】

LCT 的模板题。

为了方便,我把下标编号加 1 1 1,即将下标编号变为 1 1 1 n n n

对于 i i i,如果 i + k i ≤ n i+k_i\le n i+kin,就连一条 ( i , i + k i ) (i,i+k_i) (i,i+ki) 的边,表示从 i i i 可以弹到 i + k i i+k_i i+ki;否则就连 ( i , n + 1 ) (i,n+1) (i,n+1) 的边,表示从 i i i 可以弹出去。

这样连出来后必然是一棵树,因为虽然每个点可以连进来很多条,但只能连出去一条。

修改操作很简单,先 Cut 掉原来连的边,再连上以当前弹力系数对应的边就行了。

然后是询问操作,我们先把 n + 1 n+1 n+1 变成这棵树的跟(Makeroot 操作),然后我们把 x x x 到根的路径提取出来(Access 操作),再把 x x x 转到辅助树的根(Splay 操作),那么最后 s i z e x − 1 size_x-1 sizex1 就是答案了。

应该还是比较好懂的吧。。


【代码】

#include
#include
#include
#include
#define N 200005
using namespace std;
int n,m,a[N],fa[N],son[N][2],mark[N],Size[N];
stack<int>stk;
int Get(int x)
{
	return x==son[fa[x]][1];
}
bool Isroot(int x)
{
	if(!fa[x])  return true;
	return (son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x);
}
void Pushup(int x)
{
	Size[x]=Size[son[x][0]]+Size[son[x][1]]+1;
}
void Pushdown(int x)
{
	if(!mark[x])  return;
	if(son[x][0])  mark[son[x][0]]^=1;
	if(son[x][1])  mark[son[x][1]]^=1;
	swap(son[x][0],son[x][1]),mark[x]=0;
}
void Rotate(int x)
{
	int y=fa[x],z=fa[y];
	int k=Get(x),l=son[x][k^1];
	son[y][k]=l;fa[l]=(l?y:0);
	if(!Isroot(y))  son[z][Get(y)]=x;fa[x]=z;
	son[x][k^1]=y,fa[y]=x;
	Pushup(y),Pushup(x);
}
void Splay(int x)
{
	stk.push(x);
	for(int i=x;!Isroot(i);i=fa[i])  stk.push(fa[i]);
	while(!stk.empty())  Pushdown(stk.top()),stk.pop();
	while(!Isroot(x))
	{
		int y=fa[x];
		if(!Isroot(y))  Rotate(Get(x)==Get(y)?y:x);
		Rotate(x);
	}
}
void Access(int x)
{
	int i;
	for(i=0;x;x=fa[i=x])
	  Splay(x),son[x][1]=i,Pushup(x);
}
void Makeroot(int x)
{
	Access(x);
	Splay(x);
	mark[x]^=1;
}
void Link(int x,int y)
{
	Makeroot(x);
	fa[x]=y;
}
void Cut(int x,int y)
{
	Makeroot(x);
	Access(y);Splay(y);
	son[y][0]=fa[x]=0;
	Pushup(y);
}
int Query(int x)
{
	Makeroot(n+1);
	Access(x),Splay(x);
	return Size[x]-1;
}
void Modify(int x,int y)
{
	Cut(x,(x+a[x]<=n)?x+a[x]:n+1);
	Link(x,(x+y<=n)?x+y:n+1),a[x]=y;
}
int main()
{
	int x,y,i,op;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		Link(i,(i+a[i]<=n)?i+a[i]:n+1);
	}
	scanf("%d",&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&op,&x);
		if(op==1)  printf("%d\n",Query(++x));
		if(op==2)  scanf("%d",&y),Modify(++x,y);
	}
	return 0;
}

你可能感兴趣的:(#,LCT)