[BZOJ 2002][Luogu 3203][HNOI2010]Bounce 弹飞绵羊

[BZOJ 2002][Luogu 3203][HNOI2010]Bounce 弹飞绵羊

<题意概括>

给定一个数列$\left\{k_{N}\right\}$,数列长度为n,k次操作

要求支持两种操作

1.求最少对数x进行几次变换才能满足条件$x>n$(一次操作定义为$x=x+k_{x}$)

2.$k_{x}$修改为a

<做法>

通过建模我们可以将题目转化为如下的问题

建一个图,将图中每个顶点i和顶点$i+k_{i}$连边

那么操作一就转化为了求顶点i到任一编号大于n的顶点的最短路长度

但是这样可能有很多编号大于n的顶点,不方便统计

于是我们将编号大于n的顶点缩为一个编号为$n+1$的点

这样操作一就相当于求顶点i到顶点$n+1$的最短路长度

操作二则是断开边(i,$i+k_{i}$),$k_{i}$修改为a一条新边(i,$i+k_{i}$)

由操作二的动态断边连边可以想到使用Link-Cut Tree去维护这个图

则操作一就是MakeRoot(x),Access(n+1),Splay(n+1),此时$Answer=Size_{n+1}-1$

因为在MakeRoot(x)Access(n+1),Splay(n+1)后,$x\leftrightarrow n+1$全在以rootn+1Auxiliary Tree

Answer就是该Auxiliary TreerootSize-1(除去点n+1)

操作二就是Cut(x,x+K[x]>N?N+1:x+K[x]);K[x]=y;Link(x,x+K[x]>N?N+1:x+K[x]);

<Code>

#include<cstdio>
#include<algorithm>
using namespace std;
#define Fast register
inline char Getchar(){
    static char BUF[16384],*S=BUF,*T=BUF;
    return(S==T)&&(T=(S=BUF)+fread(BUF,1,16384,stdin),S==T)?EOF:*S++;
}
inline int Getint(){
    Fast int s=0;Fast char c=Getchar();
    while(c<48||c>57)c=Getchar();
    while(c>47&&c<58)s=s*10+c-48,c=Getchar();
    return s;
}
struct Node{
    int lson,rson,p,size;
    bool rev;
    Node():lson(0),rson(0),p(0),size(0),rev(false){}
    Node(int key):lson(0),rson(0),p(0),size(0),rev(false){}
}node[200002];
#define ls(o) node[o].lson
#define rs(o) node[o].rson
#define Fa(o) node[o].p
#define Size(o) node[o].size
#define Rev(o) node[o].rev

inline void update(const int&o){if(o)Size(o)=Size(ls(o))+Size(rs(o))+1;}

inline void push_down(const int&o){
    if(!Rev(o))return;
    swap(ls(o),rs(o));
    Rev(ls(o))^=1;
    Rev(rs(o))^=1;
    Rev(o)=false;
}

inline bool IsRoot(const int&o){return ls(Fa(o))!=o&&rs(Fa(o))!=o;}

inline void rotate(const int&o){
    Fast int p=Fa(o),gp=Fa(p);
    if(!IsRoot(p)){
        if(ls(gp)==p)ls(gp)=o;
        else rs(gp)=o;
    }
    Fa(o)=gp;
    Fa(p)=o;
    if(ls(p)==o)Fa(rs(o))=p,ls(p)=rs(o),rs(o)=p;
    else Fa(ls(o))=p,rs(p)=ls(o),ls(o)=p;
    update(p);
    update(o);
}

inline void Splay(const int&o){
    int Stack[200001];
    Fast int Top=0;
    Stack[++Top]=o;
    for(Fast int i=o;!IsRoot(i);i=Fa(i))Stack[++Top]=Fa(i);
    while(Top)push_down(Stack[Top--]);
    while(!IsRoot(o)){
        if(!IsRoot(Fa(o))){
            if(ls(Fa(o))==o^ls(Fa(Fa(o)))==Fa(o))rotate(o);
            else rotate(Fa(o));
        }
        rotate(o);
    }
}

inline void Access(int o){
    for(Fast int k=0;o;k=o,o=Fa(o)){
        Splay(o);
        rs(o)=k;
        update(o);
    }
}

inline void MakeRoot(const int&o){
    Access(o);
    Splay(o);
    Rev(o)^=1;
}

inline int Find(int o){
    Access(o);
    Splay(o);
    while(ls(o))o=ls(o);
    return o;
}

inline void Link(const int&x,const int&y){
    MakeRoot(x);
    Fa(x)=y;
}

inline void Cut(const int&x,const int&y){
    MakeRoot(x);
    Access(y);
    Splay(y);
    if(ls(y)==x)ls(y)=Fa(x)=0;
}
int K[200001];
int main(){
    Fast int N=Getint(),M,x,y,opt;
    for(Fast int i=1;i<=N;++i)K[i]=Getint(),Link(i,i+K[i]>N?N+1:i+K[i]);
    M=Getint()+1;
    while(--M){
        opt=Getint(),x=Getint()+1;
        if(opt&1){
            MakeRoot(x);
            Access(N+1);
            Splay(N+1);
            printf("%d\n",Size(N+1)-1);
        }
        else{
            y=Getint();
            Cut(x,x+K[x]>N?N+1:x+K[x]);
            K[x]=y;
            Link(x,x+K[x]>N?N+1:x+K[x]);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Trisolaris/p/BZOJ-2002.html

时间: 2024-10-18 06:01:57

[BZOJ 2002][Luogu 3203][HNOI2010]Bounce 弹飞绵羊的相关文章

【BZOJ】2002: [Hnoi2010]Bounce 弹飞绵羊(lct)

(BZOJ挂了,还没在BZOJ测,先是在wikioi测过了,,) 囧.在军训时立志要学lct!!!这是一道lct的裸题,只有access操作(10行都没有啊亲...缩行大法的话,我就不说了..)(link操作相当于水过),其实lct很简单..想想都有点小激动...... lct用splay维护的话,一下就写好了..但是我在写lct的时候,发现了一些我原来splay的老问题,我原来也知道了的,就是将null的ch给赋值了,因为在rot操作里没有特判,所以导致了null的孩子被赋值了,导致我的lct

BZOJ 题目2002: [Hnoi2010]Bounce 弹飞绵羊(link cut tree)

2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec  Memory Limit: 259 MB Submit: 5421  Solved: 2863 [Submit][Status][Discuss] Description 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹

BZOJ 2002: [Hnoi2010]Bounce 弹飞绵羊( LCT )

LCT... ---------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( int i = 0 ; i < n ; ++i ) #define clr( x , c ) memset( x

bzoj 2002: [Hnoi2010]Bounce 弹飞绵羊 動態樹

2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 4055  Solved: 2172[Submit][Status] Description 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki

【BZOJ 2002】 [Hnoi2010]Bounce 弹飞绵羊

2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec  Memory Limit: 259 MB Submit: 4204  Solved: 2255 [Submit][Status] Description 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+

bzoj 2002 : [Hnoi2010]Bounce 弹飞绵羊 (LCT)

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2002 题面: 2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 15763  Solved: 8080[Submit][Status][Discuss] Description 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开

2002: [Hnoi2010]Bounce 弹飞绵羊

二次联通门 : 2002: [Hnoi2010]Bounce 弹飞绵羊 #include <cstdio> #include <cstdlib> #define Max 200010 void read (int &now) { now = 0; register char word = getchar (); while (word < '0' || word > '9') word = getchar (); while (word >= '0' &a

【bzoj2002】[Hnoi2010]Bounce 弹飞绵羊 link-cut-tree

2016-05-30 11:51:59 用一个next数组,记录点x的下一个点是哪个 查询时,moveroot(n+1),access(x),splay(x) ,输出size[ch[x][0]]即为答案 更改时,cut(x,next[x]) link(x,min(x+k,n+1)) 记得splay旋转后要更新size 1 #include<bits/stdc++.h> 2 #define N 200005 3 using namespace std; 4 int read(){ 5 int x

AC日记——[HNOI2010]BOUNCE 弹飞绵羊 洛谷 P3203

[HNOI2010]BOUNCE 弹飞绵羊 思路: SBlct: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 200005 int n,m,f[maxn],ch[maxn][2],rev[maxn],ki[maxn],sta[maxn],top,lit,size[maxn]; inline void in(int &now) { char Cget=getchar();now=0; while(Cget&