BZOJ2002 Hnoi2010 Bounce 弹飞绵羊 【LCT】【分块】

BZOJ2002 Hnoi2010 Bounce 弹飞绵羊


Description

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

Input

第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,接下来一行有n个正整数,依次为那n个装置的初始弹力系数。第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000

Output

对于每个i=1的情况,你都要输出一个需要的步数,占一行。

Sample Input

4
1 2 1 1
3
1 1
2 1 1
1 1

Sample Output

2
3


分块做法和LCT做法都有

先讲讲LCT,就是一个板子,把当前点和可以连到的点连边,然后修改弹力值就是把边断开然后重新连一条边,维护的答案是一条链的长度,在这里可以直接用LCT的性质直接维护siz

#include<bits/stdc++.h>
using namespace std;
#define N 200010
#define INF 1e9
inline int read(){
    int res=0,w=1;char c=getchar();
    while(!isdigit(c)&&c!='-')c=getchar();
    if(c=='-')w=-1,c=getchar();
    while(isdigit(c))res=(res<<1)+(res<<3)+c-'0',c=getchar();
    return res;
}
struct Link_Cut_Tree{
    int fa[N],son[N][2],siz[N];bool rev[N];
    Link_Cut_Tree(){memset(rev,0,sizeof(rev));}
    bool isroot(int t){return son[fa[t]][0]!=t&&son[fa[t]][1]!=t;}
    bool Son(int t){return son[fa[t]][1]==t;}
    void pushup(int t){siz[t]=siz[son[t][0]]+siz[son[t][1]]+1;}
    void pushdown(int t){
        if(!isroot(t))pushdown(fa[t]);
        if(rev[t]){
            rev[t]^=1;
            rev[son[t][0]]^=1;
            rev[son[t][1]]^=1;
            swap(son[t][0],son[t][1]);
        }
    }
    void rotate(int t){
        int f=fa[t],g=fa[f];
        bool a=Son(t),b=a^1;
        if(!isroot(f))son[g][Son(f)]=t;fa[t]=g;
        son[f][a]=son[t][b];fa[son[t][b]]=f;
        son[t][b]=f;fa[f]=t;
        pushup(f);pushup(t);
    }
    void splay(int t){
        pushdown(t);
        while(!isroot(t)){
            int f=fa[t];
            if(!isroot(f)){
                if(Son(t)^Son(f))rotate(t);
                else rotate(f);
            }
            rotate(t);
        }
    }
    void access(int t){
        int tmp=0;
        while(t){
            splay(t);
            son[t][1]=tmp;
            if(tmp)fa[tmp]=t;pushup(t);
            tmp=t;t=fa[t];
        }
    }
    void putroot(int t){
        access(t);
        splay(t);
        rev[t]^=1;
    }
    void link(int x,int y){putroot(x);fa[x]=y;}
    void cut(int x,int y){
        putroot(x);
        access(y);
        splay(y);
        son[y][0]=fa[x]=0;
        pushup(y);
    }
    int query(int x,int y){
        putroot(y);
        access(x);
        splay(x);
        return siz[x]-1;
    }
}lct;
int n,m,a[N];
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)
        lct.link(i,(i+a[i]>n)?n+1:i+a[i]);
    m=read();
    for(int i=1;i<=m;i++){
        int op,x,y;
        scanf("%d",&op);
        if(op==1){
            scanf("%d",&x);x++;
            printf("%d\n",lct.query(x,n+1));
        }else{
            scanf("%d%d",&x,&y);x++;
            lct.cut(x,(x+a[x]>n)?n+1:x+a[x]);
            a[x]=y;
            lct.link(x,(x+a[x]>n)?n+1:x+a[x]);
        }
    }
    return 0;
}

然后讲讲分块,每次暴力跳,维护一下每个点跳出当前块的操作次数,暴力修改暴力查询就好了

#include<bits/stdc++.h>
using namespace std;
#define INF 0x7fffffff
#define LL long long
#define N 1000010
const int siz=1000;
struct Temp{int to,tmp,bel;}a[N];
int n,m,pre[N];
int read(){
    int ans=0,w=1;char c=getchar();
    while(!isdigit(c)&&c!='-')c=getchar();
    if(c=='-')c=getchar(),w=-1;
    while(isdigit(c))ans=ans*10+c-'0',c=getchar();
    return ans*w;
}
void modify(int x){
    if(a[x+pre[x]].bel!=a[x].bel){
        a[x].to=x+pre[x];
        a[x].tmp=1;
    }else{
        a[x].to=a[x+pre[x]].to;
        a[x].tmp=a[x+pre[x]].tmp+1;
    }
    return;
}
void build(){
    for(int i=1;i<=n;i++)a[i].bel=(i-1)/siz+1;
    for(int i=n;i>=1;i--)
        if(i+pre[i]>n)a[i].tmp=1,a[i].to=0;
        else modify(i);
}
int query(int x){
    int ans=0;
    while(x){
        ans+=a[x].tmp;
        x=a[x].to;
    }
    return ans;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)pre[i]=read();
    build();
    m=read();
    for(int i=1;i<=m;i++){
        int op=read(),x;
        if(op==1){
            x=read()+1;
            printf("%d\n",query(x));
        }else{
            x=read()+1,pre[x]=read();
            int fro=(a[x].bel-1)*siz+1;
            for(int i=x;i>=fro;i--)modify(i);
        }
    }
    return 0;
}
posted @ 2018-07-26 15:50  Dream_maker_yk  阅读(150)  评论(0编辑  收藏  举报