洛谷P3203 [HNOI2010]弹飞绵羊

洛谷P3203 [HNOI2010]弹飞绵羊

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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

思路

动态树
在初始状态下,建立动态树,将每一个节点和通过他会弹到的节点连起来,所有会弹飞的节点都连向一个\(n+1\)号点。
对于每一个修改操作,将修改的节点和他原来会弹到的节点之间断开,然后将它连向新的会弹到的节点。
对于每一个查询操作,将查询的节点和\(n+1\)号点变成实链,然后把第\(n+1\)号点splay到树顶,这是\(n+1\)号点的左子树的大小就是需要弹的次数。

CODE

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 200010
int ch[MAXN][2],fa[MAXN],cur[MAXN],sz[MAXN];
int q[MAXN];
bool rev[MAXN];
int i,j,k,m,n,x,y,qtop;
char readc;
void read(int &n){
    while((readc=getchar())<48||readc>57);
    n=readc-48;
    while((readc=getchar())>=48&&readc<=57) n=n*10+readc-48;
}
int get(int x){
    return ch[fa[x]][1]==x;
}
bool isroot(int x){
    return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
void pushup(int x){
    sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;
}
void pushdown(int x){
    if(rev[x]){
        swap(ch[x][0],ch[x][1]);
        rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
        rev[x]=false;
    }
}
void rotate(int x){
    int y=fa[x],z=fa[y],k=get(x),w=ch[x][k^1];
    ch[y][k]=w,fa[w]=y;
    if(!isroot(y)) ch[z][get(y)]=x;
    fa[x]=z;
    ch[x][k^1]=y,fa[y]=x;
    pushup(y),pushup(x);
}
void splay(int x){
    qtop=1;
    q[qtop]=x;
    for(int i=x;!isroot(i);i=fa[i]) q[++qtop]=fa[i];
    for(int i=qtop;i>=1;i--) pushdown(q[i]);
    while(!isroot(x)){
        int y=fa[x];
        if(!isroot(y)){
            if(get(x)==get(y)) rotate(y); else rotate(x);
        }
        rotate(x);
    }
    pushup(x);
}
void access(int x){
    for(int y=0;x;y=x,x=fa[x]){
        splay(x),ch[x][1]=y;
        pushup(x);
    }
}
void makeroot(int x){
    access(x);
    splay(x);
    rev[x]^=1;
}
int findroot(int x){
    access(x);
    splay(x);
    while(ch[x][0]) pushdown(x),x=ch[x][0];
    return x;
}
void split(int x,int y){
    makeroot(x);
    access(y);
    splay(y);
}
void link(int x,int y){
    makeroot(x);
    if(findroot(y)!=x) fa[x]=y;
    pushup(y);
}
void cut(int x,int y){
    makeroot(x);
    access(y);
    if(findroot(y)==x&&fa[x]==y&&ch[x][1]==0){
        fa[x]=0,ch[y][0]=0;
        pushup(y);
    }
}
int query(int x){
    split(x,n+1);
    return sz[ch[n+1][0]];
}
void update(int x,int y){
    int z=min(n+1,x+y);
    cut(x,cur[x]);
    link(x,z);
    cur[x]=z;
}
int main(){
    read(n);
    for(i=1;i<=n;i++) read(cur[i]),sz[i]=1;
    sz[n+1]=1;
    for(i=1;i<=n;i++){
        k=min(n+1,i+cur[i]);
        cur[i]=k;
        link(i,k);
    }
    read(m);
    for(i=1;i<=m;i++){
        read(x); read(y);
        if(x==1){
            printf("%d\n",query(y+1));
        }else{
            read(k);
            update(y+1,k);
        }
    }
    return 0;
}
posted @ 2019-02-02 00:23  EternalBlue  阅读(126)  评论(0编辑  收藏  举报