[HNOI2010]弹飞绵羊

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

先建树,如果i+k大于n,就和n+1,建边表示弹出,否则和i+k建边

建树用LCT的link操作

修改就cut操作删掉旧边,加上新边

查询时,把n+1变为树根,把x到n+1都变为实根

查询路径上节点数-1输出

splay维护size,因为总是只有一颗完整的树,所以不需要判断cut和link操作的可行性

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 int size[200005],ch[200005][2],rev[200005],pre[200005],isrt[200005],n,m,k[200005];
  8 void pushup(int o)
  9 {
 10   if (!o) return;
 11   size[o]=size[ch[o][0]]+size[ch[o][1]]+1;
 12 }
 13 void pushdown(int o)
 14 {
 15   if (!o) return;
 16   if (rev[o])
 17     {
 18       int ls=ch[o][0],rs=ch[o][1];
 19       rev[ls]^=1;
 20       swap(ch[ls][0],ch[ls][1]);
 21       rev[rs]^=1;
 22       swap(ch[rs][0],ch[rs][1]);
 23       rev[o]=0;
 24     }
 25 }
 26 void push(int o)
 27 {
 28   if (isrt[o]==0) push(pre[o]);
 29   pushdown(o);
 30 }
 31 void rotate(int o,bool kind)
 32 {
 33   int p=pre[o];
 34   ch[p][!kind]=ch[o][kind];pre[ch[o][kind]]=p;
 35   if (isrt[p]) isrt[p]=0,isrt[o]=1;
 36   else ch[pre[p]][ch[pre[p]][1]==p]=o;
 37   pre[o]=pre[p];
 38   ch[o][kind]=p;pre[p]=o;
 39   pushup(p);pushup(o);
 40 }
 41 void splay(int o)
 42 {
 43   push(o);
 44   while (isrt[o]==0)
 45     {
 46       if (isrt[pre[o]])
 47     rotate(o,ch[pre[o]][0]==o);
 48       else
 49     {
 50       int p=pre[o],kind=ch[pre[p]][0]==p;
 51       if (ch[p][kind]==o)
 52         rotate(o,!kind),rotate(o,kind);
 53       else rotate(p,kind),rotate(o,kind);
 54     }
 55     }
 56 }
 57 void access(int o)
 58 {
 59   int y=0;
 60   while (o)
 61     {
 62       splay(o);
 63       isrt[ch[o][1]]=1;
 64       isrt[ch[o][1]=y]=0;
 65       pushup(o);
 66       y=o;o=pre[o];
 67     }
 68 }
 69 void makeroot(int o)
 70 {
 71   access(o);
 72   splay(o);
 73   rev[o]^=1;
 74   swap(ch[o][0],ch[o][1]);
 75 }
 76 void link(int x,int y)
 77 {
 78   makeroot(x);
 79   pre[x]=y;
 80 }
 81 void cut(int x,int y)
 82 {
 83   makeroot(x);
 84   access(y);splay(y);
 85   ch[y][0]=0;pre[x]=0;
 86   isrt[x]=1;
 87   pushup(y);
 88 }
 89 int main()
 90 {int i,c,x,y,st,ed;
 91   cin>>n;
 92   for (i=1;i<=n+1;i++)
 93     isrt[i]=1,size[i]=1;
 94   for (i=1;i<=n;i++)
 95     {
 96       scanf("%d",&k[i]);
 97       if (i+k[i]>n) link(i,n+1);
 98       else link(i,i+k[i]);
 99     }
100   cin>>m;
101   for (i=1;i<=m;i++)
102     {
103       scanf("%d",&c);
104       if (c==1)
105     {
106       scanf("%d",&x);x++;
107       makeroot(n+1);
108       access(x);
109       splay(x);
110       printf("%d\n",size[x]-1);
111     }
112       else
113     {
114       scanf("%d%d",&x,&y);x++;
115       if (k[x]+x>n) st=n+1;
116       else st=k[x]+x;
117       if (y+x>n) ed=n+1;
118       else ed=y+x;
119       k[x]=y;
120       if (st!=ed)
121         {
122           cut(x,st);
123           link(x,ed);
124         }
125     }
126     }
127 }
View Code LCT

 

还有分块的做法

把n个点分成$sqrt n$块

预处理出f[i],s[i]

f[i]表示i会跳到下一块的地方

s[i]表示i跳到f[i]所需要的步数

查询时直接往后跳,最多跳$sqrt n$块

修改时只修改那一块$sqrt n$个元素

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 int n,k[300001],lim,s[300001],f[300001],m;
 8 int main()
 9 {int i,j,opt,x,now,cnt,y;
10   cin>>n;
11   for (i=1;i<=n;i++)
12     {
13       scanf("%d",&k[i]);
14     }
15   lim=sqrt(n);
16   for (i=1;i<=n;i+=lim)
17     {
18       for (j=min(n,i+lim-1);j>=i;j--)
19     {
20       if (k[j]+j>min(n,i+lim-1)) s[j]=1,f[j]=k[j]+j;
21       else s[j]=s[j+k[j]]+1,f[j]=f[k[j]+j];
22     }
23     }
24   cin>>m;
25   for (i=1;i<=m;i++)
26     {
27       scanf("%d%d",&opt,&x);x++;
28       if (opt==1)
29     {
30       cnt=0;
31       while (x<=n)
32         {
33           cnt+=s[x];
34           x=f[x];
35         }
36       printf("%d\n",cnt);
37     }
38       else
39     {
40       scanf("%d",&y);
41       k[x]=y;
42       if (x%lim==0) now=x-lim+1;
43       else now=x-x%lim+1;
44       for (j=min(n,now+lim-1);j>=now;j--)
45         {
46           if (k[j]+j>min(n,now+lim-1)) s[j]=1,f[j]=k[j]+j;
47           else s[j]=s[j+k[j]]+1,f[j]=f[k[j]+j];
48         }
49     }
50     }
51 }
View Code分块

 

posted @ 2018-01-16 16:22  Z-Y-Y-S  阅读(175)  评论(0编辑  收藏  举报