题意:有1~n个洞,每个洞i都有一个对应的值v[i],当小球落入洞i中后将弹到第i+v[i]个洞里,直到弹出所有洞。一共m次操作,0 a b表示将洞a的值修改为b,1 a表示询问,要求输出将小球投入洞a后,小球最后弹入的洞和小球弹的次数。
每次修改操作都需要更新,如果暴力一定超时。所以考虑分块处理,将n个洞分成根号n个区块,每次只需要在洞a所在的区块中更新。cnt数组记录小球弹出所在区块需要的次数,last数组记录小球在所在区块最后弹入的洞,link数组记录小球从所在区块弹入其他区块时首先弹入的洞。这样,时间复杂度由O(N²)优化为O(NlogN)。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int n,m;
int v[100005];//v[i]:第i个洞的值
int cnt[100005];//cnt[i]:跳出第i个洞所在区块所需要的步数
int last[100005];//last[i]:跳出第i个洞所在区块时最后待过的洞
int link[100005];//link[i]:从i所在区块跳出后到达的洞
int block;//每个区块中洞的个数
void update(int x, int y)//从第x个洞跳到第y个洞
{
if(y > n)//跳出去了
{
link[x] = n + 1;
last[x] = x;
cnt[x] = 1;
}
else if(x / block == y / block)//跳在同一个区块中
{
link[x] = link[y];
last[x] = last[y];
cnt[x] = cnt[y] + 1;
}
else//跳到其他区块
{
link[x] = y;
last[x] = x;
cnt[x] = 1;
}
}
void Search(int x)
{
int num,ans;
ans = cnt[x];
num = last[x];
while(1)
{
x = link[x];
if(x > n) break;
num = last[x];
ans += cnt[x];
}
printf("%d %d\n",num,ans);
}
int main()
{
int choice,x,a,b;
while(scanf("%d%d",&n,&m) != EOF)
{
block = ceil(sqrt(n));//开根后进一法取整
//cout << block << endl;
for(int i = 1; i <= n; i++)
scanf("%d",&v[i]);
for(int i = n; i > 0; i--)//从后往前更新
update(i,i + v[i]);
while(m --)
{
scanf("%d",&choice);
if(choice)
{
scanf("%d",&x);
Search(x);
}
else
{
scanf("%d%d",&a,&b);
v[a] = b;
for(int i = a; i && i / block == a / block; i--)//更新所在区块即可
update(i,i + v[i]);
}
}
}
}