bzoj2002【HNOI2010】Bounce 弹飞绵羊

2002: [Hnoi2010]Bounce 弹飞绵羊

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 7467   Solved: 3934
[ Submit][ Status][ Discuss]

Description

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

Input

第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,接下来一行有n个正整数,依次为那n个装置的初始弹力系数。第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000

Output

对于每个i=1的情况,你都要输出一个需要的步数,占一行。

Sample Input

4
1 2 1 1
3
1 1
2 1 1
1 1

Sample Output

2
3



方法一:分块

很巧妙的分块。

每个点记录跳出所在块的步数以及跳出去的位置,每次修改暴力修改所在块的信息。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 200005
using namespace std;
int n,m,block;
int a[maxn],num[maxn],f[maxn],p[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int calc(int x)
{
	int ret=0;
	for(;x;x=p[x]) ret+=f[x];
	return ret;
}
int main()
{
	n=read();
	block=round(sqrt(n));
	F(i,1,n) a[i]=read(),num[i]=(i-1)/block+1;
	D(i,n,1)
	{
		if (i+a[i]>n) f[i]=1,p[i]=0;
		else if (num[i]==num[i+a[i]]) f[i]=f[i+a[i]]+1,p[i]=p[i+a[i]];
		else f[i]=1,p[i]=i+a[i];
	}
	m=read();
	while (m--)
	{
		int opt=read(),x=read()+1;
		if (opt==1) printf("%d\n",calc(x));
		else
		{
			a[x]=read();
			D(i,x,(num[x]-1)*block+1)
			{
				if (i+a[i]>n) f[i]=1,p[i]=0;
				else if (num[i]==num[i+a[i]]) f[i]=f[i+a[i]]+1,p[i]=p[i+a[i]];
				else f[i]=1,p[i]=i+a[i];
			}
		}
	}
	return 0;
}


方法二:LCT

维护一下子树的大小,注意cut操作里要splay,因为x和y的父子关系不确定,否则会出错。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define N 200005
using namespace std;
int n,m,nxt[N],st[N];
struct LCT
{
	int ch[N][2],fa[N],sz[N];bool rev[N];
	inline bool isroot(int x)
	{
		return (ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x);
	}
	inline void pushdown(int x)
	{
		if (!rev[x]) return;
		rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;
		swap(ch[x][0],ch[x][1]);
		rev[x]=0;
	}
	inline void pushup(int x)
	{
		sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
	}
	void rotate(int x)
	{
		int y=fa[x],z=fa[y],l=ch[y][1]==x,r=l^1;
		if (!isroot(y)) ch[z][ch[z][1]==y]=x;
		fa[x]=z;fa[y]=x;fa[ch[x][r]]=y;
		ch[y][l]=ch[x][r];ch[x][r]=y;
		pushup(y);pushup(x);
	}
	void splay(int x)
	{
		int top=0;st[++top]=x;
		for(int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
		D(i,top,1) pushdown(st[i]);
		while (!isroot(x))
		{
			int y=fa[x],z=fa[y];
			if (!isroot(y))
			{
				if (ch[y][0]==x^ch[z][0]==y) rotate(x);
				else rotate(y);
			}
			rotate(x);
		}
	}
	void access(int x)
	{
		int t=0;
		while (x)
		{
			splay(x);
			ch[x][1]=t;
			t=x;x=fa[x];
		}
	}
	inline void moveroot(int x)
	{
		access(x);splay(x);rev[x]^=1;
	}
	inline void link(int x,int y)
	{
		moveroot(x);fa[x]=y;
	}
	inline void cut(int x,int y)
	{
		moveroot(x);access(y);splay(y);ch[y][0]=fa[x]=0;
	}
	inline int query(int x)
	{
		moveroot(n+1);access(x);splay(x);
		return sz[ch[x][0]];
	}
}lct;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int main()
{
	n=read();
	F(i,1,n)
	{
		int x=read();
		nxt[i]=lct.fa[i]=min(i+x,n+1);
		lct.sz[i]=1;
	}
	m=read();
	while (m--)
	{
		int opt=read(),x=read()+1;
		if (opt==1) printf("%d\n",lct.query(x));
		else
		{
			int y=read(),t=min(x+y,n+1);
			lct.cut(x,nxt[x]);lct.link(x,t);nxt[x]=t;
		}
	}
	return 0;
}


你可能感兴趣的:(分块,bzoj,LCT)