[bzoj2002]弹飞绵羊

来源:互联网 发布:unity3d 多个摄像机 编辑:程序博客网 时间:2024/04/28 08:55

题目大意

有N个点,每个点有一个系数a[i],你处于位置i可以走到i+a[i],若i+a[i]>n则你走出了地图。现M个操作有两种:1、把a[j]修改为k。2、询问你位于点j时,需要走多少部走出地图。n<=2*10^5,m<=10^5。

LCT裸题

我们可以转化为如下问题:1、将x的父亲设为y。2、询问x的深度。
那就变成了LCT裸题,为了支持询问操作只需要维护size即可。

#include<cstdio>#include<algorithm>#include<stack>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=200000+10;stack<int> sta;int father[maxn],size[maxn],pp[maxn],tree[maxn][2],a[maxn];bool bz[maxn];int i,j,k,l,t,n,m;void update(int x){    size[x]=size[tree[x][0]]+size[tree[x][1]]+1;}int pd(int x){    return tree[father[x]][1]==x;}void rotate(int x){    int y=father[x],z=pd(x);    tree[y][z]=tree[x][1-z];    if (tree[x][1-z]) father[tree[x][1-z]]=y;    father[x]=father[y];    if (father[y]) tree[father[y]][pd(y)]=x;    tree[x][1-z]=y;    father[y]=x;    update(y);    update(x);    if (pp[y]) pp[x]=pp[y],pp[y]=0;}void clear(int x){    if (bz[x]){        bz[x]=0;        if (tree[x][0]) bz[tree[x][0]]^=1;        if (tree[x][1]) bz[tree[x][1]]^=1;        swap(tree[x][0],tree[x][1]);    }}void remove(int x,int y){    while (x!=y){        sta.push(x);        x=father[x];    }    while (!sta.empty()){        clear(sta.top());        sta.pop();    }}void splay(int x,int y){    remove(x,y);    while (father[x]!=y){        if (father[father[x]]!=y)            if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);        rotate(x);    }}void access(int x){    int y;    splay(x,0);    father[tree[x][1]]=0;    if (tree[x][1]) pp[tree[x][1]]=x;    tree[x][1]=0;    update(x);    while (pp[x]){        y=pp[x];        splay(y,0);        father[tree[y][1]]=0;        if (tree[y][1]) pp[tree[y][1]]=y;        tree[y][1]=x;        father[x]=y;        pp[x]=0;        update(y);        splay(x,0);    }}void makeroot(int x){    access(x);    splay(x,0);    bz[x]^=1;}void cut(int x,int y){    access(x);    splay(y,0);    pp[y]=0;}void link(int x,int y){    makeroot(x);    splay(y,0);    pp[y]=x;    makeroot(n+1);}int main(){    scanf("%d",&n);    fo(i,1,n){        scanf("%d",&a[i]);        j=(i+a[i]>n)?n+1:i+a[i];        link(j,i);    }    scanf("%d",&m);    while (m--){        scanf("%d%d",&t,&j);        j++;        if (t==1){            access(j);            splay(j,0);            printf("%d\n",size[tree[j][0]]);        }        else{            scanf("%d",&k);            l=(j+a[j]>n)?n+1:j+a[j];            cut(l,j);            a[j]=k;            l=(j+a[j]>n)?n+1:j+a[j];            link(l,j);        }    }}

不过我们有更好的方法。

分块大法好

我们分块后,对每一个元素维护两个东西:b[i]表示i一直跳跳出自己所在块后跳到了哪个点,d[i]表示i跳出自己所在块的所需步数。有了这两个数组就可以进行询问,而修改操作暴力重建整个块就好了。

#include<cstdio>#include<algorithm>#include<cmath>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;const int maxn=200000+10;int a[maxn],b[maxn],d[maxn];int i,j,k,l,t,n,m,ans,c;int main(){    scanf("%d",&n);    c=floor(sqrt(n))+1;    fo(i,1,n) scanf("%d",&a[i]);    fd(i,n,1){        k=min(((i-1)/c+1)*c,n);        if (i+a[i]>k) d[i]=1,b[i]=i+a[i];        else d[i]=d[i+a[i]]+1,b[i]=b[i+a[i]];    }    scanf("%d",&m);    while (m--){        scanf("%d",&t);        if (t==1){            scanf("%d",&j);            j++;            ans=0;            while (j<=n){                ans+=d[j];                j=b[j];            }            printf("%d\n",ans);        }        else{            scanf("%d%d",&j,&k);            j++;            a[j]=k;            k=min(((j-1)/c+1)*c,n);            fd(i,k,(j-1)/c*c+1){                if (i+a[i]>k) d[i]=1,b[i]=i+a[i];                else d[i]=d[i+a[i]]+1,b[i]=b[i+a[i]];            }        }    }}
1 0