题目大意:给你n个洞,进入某个洞后会跑到另一个洞,到了另一个洞之后又可能会继续到下一个洞,问你从一个洞进去,钻了几个洞才会出来,在哪个洞出来
n 个整数a[i] 表示进入i这个洞之后会跑到 i+a[i]....
当然,题目不会这么简单,还有一个操作就是更新某个点的power,也就是a[i]的值。
直接模拟的话肯定不行
想了想,询问可以变成O(1),但是更新操作代价是O(n)的,复杂度还是m*n
那我们能不能做一些平衡呢,比如让询问的复杂度增大一点,以降低更新的复杂度,答案自然是可以的,呵呵
考虑到如果用两个数组标记一下end[i]表示进入i后最终到的那个洞,cnt[i]表示i到最终到的那个洞花了几步,这样的话,每次更新一个数,它之前的所有的end[i],cnt[i]都有可能要更新,但实际上前面很多地方走一小段距离到达的位置和步数其实是不变的,如果更新了后半段某个位置的值,那其实如果end cnt 的最终界限划在了n/2的位置,前面的end cnt都是没必要变的!!因此我们可以将数组分段,分成若干段分别维护end 和 cnt ,另外需要加一个jmp数组,jmp[i]表示跳出i所在的段后 到达的另一段中的位置。
那分成几段好呢,假设分成x段,那么询问的复杂度就是n/x,更新的复杂度就是X,总复杂度是m*(n/x+x),显然这个X取sqrt(n)最为合适、、、
#include<cstdio>
#include<cstring>
const int maxn = 100010;
const int BLOCK = 330;
int jmp[maxn] ;//jmp[i]表示跳出i所在的BLOCK后 一步 到达的另一个BLOCK中的某个位置
int end[maxn];
int cnt[maxn];
int a[maxn];
int n;
void go(int now)
{
int ans = 0;
while(jmp[now]!=maxn)
{
ans += cnt[now];
now = jmp[now];
}
ans+=cnt[now];
printf("%d %d\n",end[now],ans);
}
void update(int i,int j)
{
if(j>n)
{
end[i] = i;
jmp[i] = maxn;
cnt[i] = 1;
}
else
{
end[i] = end[j];
if(i/BLOCK==j/BLOCK)
{
jmp[i] = jmp[j];
cnt[i] = cnt[j] + 1;
}
else
{
jmp[i] = j;
cnt[i] = 1;
}
}
}
int main()
{
int m,op,x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=n;i>=1;i--)
{
update(i,i+a[i]);
}
for(int i=0;i<m;i++)
{
scanf("%d",&op);
if(!op)
{
scanf("%d%d",&x,&y);
a[x] = y;
update(x,x+a[x]);
int start = x/BLOCK*BLOCK;
for(int i=x-1;i>=start;i--)
{
update(i,i+a[i]);
}
}
else
{
scanf("%d",&x);
go(x);
}
}
return 0;
}