题目大意:
给你n个编号1到N的洞(N<=1e5)。
每一个洞有个能量值po[i]。当你在i号洞里放一个球时.这个球会弹到i+po[i]号洞内。
然后弹到i+po[i]+po[i+po[i]]......也就是说球每到一个洞i就会弹到i+po[i]号洞内。如今有两种操作。
1.0 a b : 把a号洞的po改成b
2.1 a : 询问当把球放到a号洞内时。它是从几号洞弹出界的。和它一共弹了几次。
分析:
首先,我们先考虑没有改动操作,那么不停的用i+a[i] 的操作可以找到大于N的位置;再用回溯的方式,我们可以很快就可以一一找到每个点最终的答案。
那么当考虑改动时,我们可以把序列分为sqrt(N)块,再不停用i+a[i]的操作找到i跳到后面若干块的位置,而修改时,我们最多只要对sqrt(N)个数据进行暴力就可以完成。
算法的复杂度O(sqrt(n)*m)
//code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 1000;
const int sq = 316;
int a[maxn];
int dp[5*maxn];
int vis[5*maxn];
int ti[5*maxn];
vector<int>ver[sq + 10];
void update(int x, int n){
int yc = x;
int r = ((x - 1)/sq)*sq + sq;
int l = ((x - 1)/sq)*sq + 1;
if(yc+a[yc] <= r&&yc + a[yc] <= n){
dp[yc] = dp[yc + a[yc]];
ti[yc] = ti[yc + a[yc]] + 1;
}else {
dp[yc] = yc + a[yc];
ti[yc] = 1;
}
fill(vis + l + 1, vis + yc + 1, 0);
for(int i = yc; i >= l; i--){
if(a[i] + i == yc){
vis[i] = 1;
dp[i] = dp[yc];
ti[i] = ti[yc]+1;
}else if(i+a[i] < yc && vis[i+a[i]] == 1){
dp[i] = dp[yc];
vis[i] = 1;
ti[i] = ti[i+a[i]] + 1;
}
}
}
void query(int x, int n, int &ans1, int &ans2){
while(dp[x] <= n){
ans2 = ans2 + ti[x];
x = dp[x];
// cout<<"yes\n";
}
ans2 = ans2 + ti[x];
while(x <= n){
ans1 = x;
x = x + a[x];
// cout<<"yes\n";
}
}
int main(){
int n, m, op, x, y;
cin>>n>>m;
// fill(vis, vis+maxn, 0);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
stack<int>sta;
for(int i = 1; i <= n; i++){
int r = ((i - 1)/sq)*sq + sq;
int yc = i;
if(vis[yc]) continue;
while(yc <= n&& yc <= r){
sta.push(yc);
yc = yc + a[yc];
}
int la = 0;
while(!sta.empty()){
dp[sta.top()] = yc;
ti[sta.top()] = la + 1;
la = ti[sta.top()];
vis[sta.top()] = 1;
sta.pop();
}
}
while(m--){
scanf("%d", &op);
// for(int i = 1; i <= n; i++) cout<<dp[i]<<" ";
// cout<<endl;
if(op == 0){
scanf("%d%d", &x, &y);
a[x] = y;
update(x, n);
}else{
scanf("%d", &x);
int ans2 = 0; int ans1;
query(x, n, ans1, ans2);
printf("%d %d\n", ans1, ans2);
}
}
return 0;
}