好的,我们先来看题:
有N个洞,每个洞有相应的弹力,能把这个球弹到i+power[i] 位置。当球的位置>N时即视为被弹出
输入: 第一行两个正整数N,M,下面N个数代表初始的power[i] 之后M行分别代表M个操作 共有两种操作: 0 a b 把a位置的弹力改成b 1 a 在a处放一个球,输出球被弹出前共被弹了多少次,最后一次落在哪个洞。
输出:对于每个操作2,输出两个题目中要求的值(空格隔开)
1<=N<=100000 1<=M<=100000
输入样例#1: 复制
8 5 1 1 1 1 1 2 8 2 1 1 0 1 3 1 1 0 3 4 1 2
输出样例#1: 复制
8 7 8 5 7 3
其实这道题和弹飞绵羊区别并不大,同样也是利用分块来解决,只不过还要求离开前的最后位置。我们还是简单的理顺一下这道题的思路吧:
思路其实很简单,我们将整个序列分成若干个块(块的大小取决于数据,但是我们一般都会默认为√n),对于每一块的每一个点我们只要维护他弹出这个块还需几步和弹出之后会到达哪一个点即可。
关于修改,由于每一个块之间都是相互独立的,所以单点修改只需要维护每一个块就可以了,并且由于每一个点的数据都建立在他后面块的基础上,所以我们对于每一个块都需要倒着来进行维护。
而对于查询次数我们只要循着每一个点的指向,累加每一个点的sum值,大于n的时候跳出即可。
可是如果要查询位置呢?
这里就隐藏着一个巨大的坑点:由于每一个点记录的只是弹出这个方块所到达的位置,所以求得的最终位置并不是实际的位置,还要再往后找一遍(就在一个块内,时间复杂度可以忽略)就完事了!
最后,附上本题代码:
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 struct pot 6 { 7 int to,k,conti,sum; 8 }p[100005]; 9 int size,cnt=1,n,ac,ans; 10 void change(int ct) 11 { 12 //printf("%d ",size); 13 for(int j=min(ct*size,n);j>(ct-1)*size;j--) 14 { 15 if(j + p[j].k > ct * size || j + p[j].k > n) p[j].sum = 1,p[j].to = j + p[j].k; 16 else 17 { 18 p[j].to=p[j+p[j].k].to; 19 p[j].sum=p[j+p[j].k].sum + 1; 20 } 21 //printf("%d ",p[j].to); 22 } 23 } 24 void query(int y) 25 { 26 ans=0; 27 while(y<=n) 28 { 29 ans+=p[y].sum; 30 //printf("%d ",ans); 31 if(p[y].to>n) ac=y; 32 y=p[y].to; 33 } 34 while(ac+p[ac].k<=n) 35 { 36 ac+=p[ac].k; 37 } 38 } 39 int main() 40 { 41 int temp = 0,m; 42 scanf("%d%d",&n,&m); 43 size=sqrt(n); 44 for(int i=1;i<=n;i++) 45 { 46 temp++; 47 if(temp>size) 48 { 49 cnt++; 50 temp=1; 51 } 52 scanf("%d",&p[i].k); 53 p[i].conti=cnt; 54 } 55 //if(temp != 0) cnt++; 56 //printf("%d ",cnt); 57 for(int i=cnt;i >= 1;i--) 58 change(i); 59 for(int i=1;i<=m;i++) 60 { 61 int x,y,z; 62 scanf("%d%d",&x,&y); 63 if(x==0) 64 { 65 scanf("%d",&z); 66 p[y].k=z; 67 //printf("%d ",p[y].k); 68 change(p[y].conti); 69 } 70 else 71 { 72 query(y); 73 printf("%d %d ",ac,ans); 74 } 75 } 76 return 0; 77 }