題意:
給你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; }