BZOJ 2002 [Hnoi2010]Bounce 弹飞绵羊:分块
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2002
题意:
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。
游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。
绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。
为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,接下来一行有n个正整数,依次为那n个装置的初始弹力系数。第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。
n<=200000,m<=100000
题解:
分块。
对于每个元素,维护两个值:
nex[i]:表示从当前点第一次跳到下一个块的点的位置。
stp[i]:表示跳出块要用的步数。
update在一个块内暴力从后往前维护。
query不断地跳区间就行了。
两个操作都是单次O(sqrt(N))。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <math.h> 5 #define MAX_N 200005 6 7 using namespace std; 8 9 int n,m; 10 int len,cnt; 11 int k[MAX_N]; 12 int pos[MAX_N]; 13 int nex[MAX_N]; 14 int stp[MAX_N]; 15 16 void read() 17 { 18 scanf("%d",&n); 19 for(int i=1;i<=n;i++) 20 { 21 scanf("%d",&k[i]); 22 } 23 } 24 25 void init_block() 26 { 27 len=sqrt(n)+1; 28 cnt=ceil((double)n/(double)len); 29 for(int i=1;i<=n;i++) 30 { 31 pos[i]=(i-1)/len+1; 32 } 33 for(int i=n;i>0;i--) 34 { 35 if(i+k[i]>n) nex[i]=0,stp[i]=1; 36 else if(i+k[i]>pos[i]*len) nex[i]=i+k[i],stp[i]=1; 37 else nex[i]=nex[i+k[i]],stp[i]=stp[i+k[i]]+1; 38 } 39 } 40 41 void update(int x,int y) 42 { 43 k[x]=y; 44 for(int i=min(pos[x]*len,n);i>(pos[x]-1)*len;i--) 45 { 46 if(i+k[i]>n) nex[i]=0,stp[i]=1; 47 else if(i+k[i]>pos[i]*len) nex[i]=i+k[i],stp[i]=1; 48 else nex[i]=nex[i+k[i]],stp[i]=stp[i+k[i]]+1; 49 } 50 } 51 52 int query(int x) 53 { 54 int sum=0; 55 while(x) 56 { 57 sum+=stp[x]; 58 x=nex[x]; 59 } 60 return sum; 61 } 62 63 void work() 64 { 65 init_block(); 66 scanf("%d",&m); 67 int opt,x,y; 68 while(m--) 69 { 70 scanf("%d%d",&opt,&x); 71 if(opt==1) printf("%d\n",query(x+1)); 72 else 73 { 74 scanf("%d",&y); 75 update(x+1,y); 76 } 77 } 78 } 79 80 int main() 81 { 82 read(); 83 work(); 84 }