题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2002
题目大意:
中文题面,不多解释。
解题思路:
讲道理本来是在分块专题里面做的这道题,然后看了半天,这跟分块有个毛线的关系。这也是我第一次做的分块题,惨,第一次就没想出来做法。
这里先谈一下分块算法,分块算法呢其实某种程度上是一个暴力算法,思路呢就是将题目给的东西分成根号n个块,然后每个块维护一个值,更新这个块内数据的时候,只遍历这个块内的数据,这样下来的话总体的复杂度是n根号n的算法,也算是一个极其暴力但是很方便的算法。
这道题呢比较奇怪,反正我刚开始没看出跟分块有啥关系。。。题意很清晰,每次球到一个点后会往后弹a【i】个点,问多少次可以弹出范围,首先还是正常的分块,分块之后呢我们对于每个点维护两个值,一个是从当前点开始跳到下一块需要跳几步,其次再维护从当前点会跳到下一个块的哪一个点,这些都维护好之后,每次查询直接加上当前点的次数,然后将当前点的位置更新到下一个块,这样累积最后求答案即可。更新操作就更新可能受到影响的点的即可,以下贴代码,
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005;
int belong[N],block,num,l[N],r[N],x[N],y[N];
//x数组为跳到下一个块需要的次数 y数组为从前点能跳到下一个块的位置
int a[N],n,m;
void build()
{
block=sqrt(n); //正常分块
num=n/block;if(n%block) num++;
for(int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block;
r[num]=n;
for(int i=1;i<=n;i++)
belong[i]=(i-1)/block+1;
for(int i=n;i>=1;i--) //求出x数组和y数组
{
if(i+a[i]>r[belong[i]])
{
x[i]=1;
y[i]=i+a[i];
}
else
{
x[i]=x[i+a[i]]+1;
y[i]=y[i+a[i]];
}
}
}
void update(int s,int k)
{
a[s]=k;
for(int i=s;i>=r[belong[s]-1];i--) //更新需要更新的点即可
{
if(i+a[i]>r[belong[i]])
{
x[i]=1;
y[i]=i+a[i];
}
else
{
x[i]=x[i+a[i]]+1;
y[i]=y[i+a[i]];
}
}
}
int query(int s)
{
int res=0; //查询操作
while(s<=n)
{
res+=x[s];
s=y[s];
}
return res;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build();
scanf("%d",&m);
int s,p,q;
for(int i=1;i<=m;i++)
{
scanf("%d",&s);
if(s==1)
{
scanf("%d",&p);
printf("%d\n",query(p+1)); //注意p要+1
}
if(s==2)
{
scanf("%d%d",&p,&q);
update(p+1,q);
}
}
}
}