題意:有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]);
}
}
}
}