bzoj2002 [Hnoi2010]Bounce 弹飞绵羊——分块

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2002

第一次用分块,感觉超方便啊;

如果记录每个点的弹力系数,那么是O(1)修改O(n)查询;

如果记录每个点几次被弹飞,那么是O(n)修改O(1)查询;

那么如果分成根号n块,则相当于每块都路径压缩,修改和查询都是根号n的复杂度!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int const maxn=200005;
int n,m,k[maxn],nxt[maxn],t[maxn],bh[maxn],K;
void change(int x)
{
//    int s=0,nw=x;
//    while(bh[nw]==bh[x]&&nw<=n)s++,nw+=k[nw];
//    t[x]=s;nxt[x]=nw;
    int y=x+k[x];
    if(bh[y]!=bh[x])t[x]=1,nxt[x]=y;
    else t[x]=t[y]+1,nxt[x]=nxt[y];
}
int query(int x)
{
//    if(x>n)return 0;
//    int ret=t[x];
//    ret+=query(nxt[x]);
//    return ret;
    int ret=0;
    while(x<=n)ret+=t[x],x=nxt[x];
    return ret;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&k[i]);
    K=sqrt(n);
    for(int i=n;i;i--)
    {
//        if(i>tot*K)tot++;bh[i]=tot;
        bh[i]=(i-1)/K+1;
//        int j=i,s=0;
//        while(bh[j]==bh[i]&&j<=n)j+=k[j],s++;
//        t[i]=s;nxt[i]=j;
        int j=i+k[i];
        if(bh[j]!=bh[i])t[i]=1,nxt[i]=j;
        else t[i]=t[j]+1,nxt[i]=nxt[j];
    }
    scanf("%d",&m);
    int x,y,z;
    while(m--)
    {
        scanf("%d%d",&x,&y);y++;
        if(x==1)printf("%d\n",query(y));
        else
        {
            scanf("%d",&z);
            k[y]=z;
//            for(int i=(bh[y]-1)*K+1;i<=y;i++)change(i);
            for(int i=y;i>(bh[y]-1)*K;i--)change(i);
        }
    }
    return 0;
}

 

posted @ 2018-06-11 17:33  Zinn  阅读(146)  评论(0编辑  收藏  举报