题意:
给你n个编号1到N的洞(N<=1e5)。
每个洞有个能量值po[i]。指针
当你在i号洞里放一个球时.这个球会弹到i+po[i]号洞内。code
而后弹到i+po[i]+po[i+po[i]]。。orm
。。也就是说球每到一个洞i就会弹到i+po[i]号洞内。blog
现在有两种操做。ip
1.0 a b。把a号洞的po改为b
2.1 a。
询问当把球放到a号洞内时。它是从几号洞弹出界的。和它一共弹了几回。
思路:
假设题目没有改动操做。这题就会很是easy。
咱们仅仅需要把每个洞的出界点和弹跳数预处理出来就可以了。现在关键是怎么处理改动操做。假设仍是依照上述预处理方式确定时间复杂度下不来。
因此咱们要想办法使每次改动操做更新尽可能少的信息。因而可以想到分块处理。就是把整个序列分红sqrt(N)块。序列中每个节点记录next[i]表示i结点要跳到下个块的位置。ed[i]表示放到i号洞时的出界位置。st[i]表示。
i跳到下个块需要的步数。这样预处理后每次改动操做仅仅会影响同块内且标号比当前小的位置的信息。
因此最多改动sqrt(n)个位置的信息。对于每次查询操做。由于结点指针仅仅会指向不一样的块因此仅仅需要顺着指针统计一遍就行了。最多跳sqrt(n)次。
总时间发杂度O(M*sqrt(N))在可以接受的范围内。
具体见代码:
#include<bits/stdc++.h> using namespace std; const int INF=0x3f3f3f3f; const int maxn=100010; typedef long long ll; int st[maxn],next[maxn],ed[maxn],po[maxn]; int block,n,m,ans,ansp; void update(int x,int y) { if(y>n) ed[x]=x,st[x]=1,next[x]=y; else { ed[x]=ed[y];//ed[x]为从x处放球的终点 if(x/block==y/block) next[x]=next[y],st[x]=st[y]+1;//next[i]表示i跳到另外的块的位置 else next[x]=y,st[x]=1; } } void qu(int x) { ans=0; while(1) { ans+=st[x]; if(next[x]>n) { ansp=ed[x]; break; } x=next[x]; } } int main() { int i,cmd,a,b; while(~scanf("%d%d",&n,&m)) { block=ceil(sqrt(1.0*n)); for(i=1;i<=n;i++) scanf("%d",&po[i]); for(i=n;i>=1;i--) update(i,i+po[i]); while(m--) { scanf("%d%d",&cmd,&a); if(cmd) { qu(a); printf("%d %d\n",ansp,ans); } else { scanf("%d",&po[a]); b=(a/block)*block; b=max(b,1); for(i=a;i>=b;i--) update(i,i+po[i]); } } } return 0; }