BZOJ2002&&P3203 [HNOI2010]弹飞绵羊

分块好题

我们将序列划分成sqtr(n)个小块,维护每个块内的东西

我们维护两个值to[i]和f[i],to[i]表示当前点能到达的第一个块外的点,f[i]表示需要多少次能跳出去,难么显然,我们查询一个点的时候,只需要通过to[i]最多转移sqrt(n)次就能跳出去,修改的话,我们不难发现,一个点,只有一个指向关系和一个被指向关系,暴力修改就好了

代码

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=3e5+10;
inline int read()
{
    int x=0;char ch=getchar();
    while(ch>'9'||ch<'0')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
int n,m,block,cnt;
int l[1005],r[1005];
int to[M],f[M],a[M],bloc[M];
inline int cal(int x)
{
    int tmp=0;
    while(x) tmp+=f[x],x=to[x];
    return tmp;
}
inline void change(int x,int y)
{
    a[x]=y;
    for(int i=x;i>=l[bloc[x]];i--)
        if(bloc[i]==bloc[i+a[i]])
            f[i]=f[i+a[i]]+1,to[i]=to[i+a[i]];
                else f[i]=1,to[i]=i+a[i];
    return ;
}
int main()
{
    n=read();block=sqrt(n);
    for(int i=1;i<=n;i++) a[i]=read();
    if(n%block) cnt=n/block+1;
    else cnt=n/block;
    for(int i=1;i<=cnt;i++)
        l[i]=(i-1)*block+1,r[i]=i*block;
    r[cnt]=n;
    for(int i=1;i<=n;i++)
       bloc[i]=(i-1)/block+1;
    for(int i=n;i>0;i--)
    {
        if(i+a[i]>n)f[i]=1;
        else if(bloc[i]==bloc[i+a[i]])
            f[i]=f[i+a[i]]+1,to[i]=to[i+a[i]];
        else f[i]=1,to[i]=i+a[i];
    }
    m=read();
    for(int i=1;i<=m;i++)
    {
        int flag=read(),x=read(),y;x++;
        if(flag==1)printf("%d\n",cal(x));
        else y=read(),change(x,y);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/acerandaker/article/details/81020599
今日推荐