您的位置:首页 > 其它

bzoj 2002: [Hnoi2010]Bounce 弹飞绵羊 動態樹

2014-10-10 11:10 309 查看

2002: [Hnoi2010]Bounce 弹飞绵羊

Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 4055 Solved: 2172
[Submit][Status]

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

HINT

  第一次編LCT,主要還是照着網上的標程在編,LCT的主體思路是對於每一個鏈維護一個splay,而非常巧妙的地方是splay的pnt[]指針,對於當前鏈上對應根節點,pnt指針等同於樹鏈剖分中的top指針,這樣可以巧妙地維護splay森林中每一顆子樹所代表的鏈的關係。
  這個LCT支持樹的刪邊加邊。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<string>
#include<queue>
using namespace std;
#ifdef WIN32
#define LL "%I64d"
#else
#define LL "%lld"
#endif
#define MAXN 310000
#define MAXV MAXN*2
#define MAXE MAXV*2
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3fLL
//AC
typedef long long qword;
inline int nextInt()
{
char ch;
int x=0;
bool flag=false;
do
ch=(char)getchar(),flag=(ch=='-')?true:flag;
while(ch<'0'||ch>'9');
do x=x*10+ch-'0';
while (ch=(char)getchar(),ch<='9' && ch>='0');
return x*(flag?-1:1);
}

int n,m;
int ch[MAXN][2],pnt[MAXN];
int siz[MAXN];
bool rt[MAXN];
bool is_root(int cur)
{
return pnt[cur]==0 || (cur!=ch[pnt[cur]][0] && cur!=ch[pnt[cur]][1]);
}

void push_up(int cur)
{
siz[cur]=siz[ch[cur][0]]+siz[ch[cur][1]]+1;
}
void rotate(int cur)
{
int p=pnt[cur],anc=pnt[p];
int dir=ch[p][0]==cur;
if (!is_root(p))
ch[anc][ch[anc][1]==p]=cur;

pnt[cur]=anc;
pnt[ch[cur][dir]]=p;
ch[p][1-dir]=ch[cur][dir];
ch[cur][dir]=p;
pnt[p]=cur;
push_up(p);
push_up(cur);
}

//int stack[MAXN];
void splay(int cur)
{
int now;
/*
int tops=-1;
now=cur;
while (pnt[now])
stack[++tops]=now;
int i;
for (i=tops;i>=0;i--)
push_down(i);
*///本題不用打標記
while (!is_root(cur))
{
int p=pnt[cur],anc=pnt[p];
if (is_root(pnt[cur]))
rotate(cur);
else if ( (ch[p][1]==cur) == (ch[anc][1]==p) )
rotate(p),rotate(cur);//Attention!
else
rotate(cur),rotate(cur);
}
push_up(cur);
}
void Access(int cur)
{
int son=0;
for (;cur;cur=pnt[son=cur])
{
splay(cur);
ch[cur][1]=son;
push_up(cur);
}
}
int a[MAXN];
int main()
{
freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
int i,j,k;
int x,y,z;
scanf("%d",&n);
for (i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for (i=1;i<=n;i++)
siz[i]=1;
siz[n+1]=1;
for (i=1;i<=n;i++)
{
int t=i+a[i];
if (t>n+1)t=n+1;
pnt[i]=t;//初始化時不存在重鏈
}
scanf("%d",&m);
int opt;
while (m--)
{
scanf("%d",&opt);
if (opt==1)
{
scanf("%d",&x);
x++;//編號從0開始
Access(x);
splay(x);//保證siz[x]包含整棵子樹
printf("%d\n",siz[x]-1);
//printf("%d\n",siz[ch[x][0]]); 二者等價
}else
{
scanf("%d%d",&x,&y);
x++;
Access(x);
splay(x);//意味着ch[x][1]==null
//此時pnt[x]不在這顆splay上
pnt[ch[x][0]]=pnt[x];
pnt[x]=0;
ch[x][0]=0;
push_up(x);//對於x即其子節點有所改動時使用
int t=x+y;;
if (t>n+1)t=n+1;
pnt[x]=t;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: