HNOI2010 弹飞绵羊

codevs 2333 弹飞绵羊

http://codevs.cn/problem/2333/

2010年省队选拔赛湖南

题目描述 Description

Lostmonkey发明了一种超级反弹装置。为了在绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿一条直线摆放 n个反弹装置,并按从前往后的方式将反弹装置依次编号为 0 到 n-1,对 0≤i≤n-1,为第 i 个反弹装置设定了初始弹力系数 ki,当绵羊落到第 i 个反弹装置上时,它将被往后弹出 ki 步,即落到第 i+ki 个反弹装置上,若不存在第i+ki个反弹装置,则绵羊被弹飞。绵羊想知道: 从第i个反弹装置开始, 它被弹出几次 (含被弹飞的那次)后会被弹飞。为使游戏更有趣,Lostmonkey 还可以修改某个反弹装置的弹力系数,但任何时候弹力系数均为正整数。

输入描述 Input Description

输入文件第一行是一个整数n,表示地上摆放n个反弹装置,输入文件第二行是用空格隔开的n个正整数k0,k1,…,kn-1,分别表示n个反弹装置的初始弹力系数。输入文件第三行是一个正整数m,表示后面还有m行输入数据。接下来的m行,每行至少有用空格隔开的两个整数i和j,若i=1,则你要输出从第j个反弹装置开始,被弹出几次后会被弹飞;若i=2,则该行有用空格隔开的三个整数i,j和k,表示第j个反弹装置的弹力系数被修改为k。

输出描述 Output Description

包含的行数等于输入文件最后m行中i=1的行数。第h行输出一个整数,表示对输入中给出的第h个求弹出次数的问题,基于n个反弹装置当时的弹力系数,求出的弹出次数。

样例输入 Sample Input


1 2 1 1

1 1 
2 1 1 
1 1

样例输出 Sample Output


3

数据范围及提示 Data Size & Hint

输入的数据保证20%的数据满足n,m≤10000。100%的数据满足n≤200000,m≤100000

用LCT维护子树大小

每弹跳一次,树中+1个节点(本次弹跳终点),这样弹跳次数就转化为了子树的大小

反弹装置从0——n-1,为了方便处理,全体后移一位

最初,对于每个反弹装置i,由i向min(n+1,i+k[i])连一条边,i指向min(n+1,i+k[i])

即这里从树根到往下是弹跳的逆过程

这样的好处是固定了弹飞点

#include<cstdio>
#include<algorithm>
#define N 200010
using namespace std;
int fa[N],ch[N][2],tag[N],siz[N],k[N],st[N],next[N];
int n,m;
inline void up(int x)
{
    siz[x]=siz[ch[x][1]]+siz[ch[x][0]]+1;
}
inline void down(int x)
{
    if(tag[x])
    {
        tag[x]^=1;tag[ch[x][0]]^=1;tag[ch[x][1]]^=1;
        swap(ch[x][0],ch[x][1]);
    }
}
inline bool isroot(int x)
{
    return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
inline void rotate(int x)
{
    int y=fa[x],z=fa[y],l,r=0;
    if(ch[y][0]==x) l=0; else l=1;
    r=l^1;
    if(!isroot(y))
    {
        if(ch[z][0]==y) ch[z][0]=x; else ch[z][1]=x;
    }
    ch[y][l]=ch[x][r];ch[x][r]=y;
    fa[y]=x;fa[ch[y][l]]=y;fa[x]=z;
    up(y);
}
inline void splay(int x)
{
    int t=0;st[++t]=x;
    for(int i=x;!isroot(i);i=fa[i]) st[++t]=fa[i];
    for(int i=t;i;i--) down(st[i]);
    while(!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if(!isroot(y))
        {
            if(ch[y][0]==x^ch[z][0]==y) rotate(x);
            else rotate(y);
        }
        rotate(x);
        up(x);
    }
}
inline void access(int x)
{
    int t=0;
    while(x)
    {
        splay(x);
        ch[x][1]=t;
        t=x;x=fa[x];
    }
}
inline void make_root(int x)
{
    access(x);
    splay(x);
    tag[x]^=1;
}
inline void link(int x,int y)
{
    make_root(x);
    fa[x]=y;
    splay(x);
}
inline void cut(int x,int y)
{
    make_root(y);
    access(x);
    splay(x);
    ch[x][0]=fa[y]=0;
}
int main()
{
    scanf("%d",&n);
    int x;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        fa[i]=x+i;siz[i]=1;
        if(fa[i]>n+1)fa[i]=n+1;
        next[i]=fa[i];
    }
    siz[n+1]=1;
    int p,y;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&p);
        if(p==1)
        {
            scanf("%d",&x);
            make_root(n+1);
            x++;
            access(x);splay(x);printf("%d\n",siz[ch[x][0]]);
        } 

        else
        {
            scanf("%d%d",&x,&y);x++;
            int t=min(n+1,x+y);
            cut(x,next[x]);link(x,t);next[x]=t;
        }
    }
}
时间: 2024-10-08 09:58:12

HNOI2010 弹飞绵羊的相关文章

Hnoi2010弹飞绵羊题解LCT

题目大意 给定一个序列,每个点有一个权值a[i],一只绵羊若站在点i上会被弹到第i+a[i]个点上,支持单点修改操作,求从某个点出发经过多少次会被弹飞. 题解 令每个点的父亲结点是会被弹到的结点,那询问时每个点的答案就是这个点的深度. LCT维护size域即可 一开始以为是有向树,后来发现自己犯2了,按无向的做法就可以. 做这种题千万别手残.我因为一句x->rever()打成x->rev^=1,就有了如下结果,调了快一整天了! Code #include <cstdio> #inc

洛谷P3203 [HNOI2010]弹飞绵羊【LCT】

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

BZOJ 2002 HNOI2010 弹飞绵羊 分块

题目大意及LCT版本题解:见 http://blog.csdn.net/popoqqq/article/details/38849471 今天手滑用分块又重写了一遍这道题0.0 分块就是短啊 将弹簧分为√n块 对于每个弹簧 我们记录一下从这个弹簧出发直到弹到块外为止的弹跳次数及落点 查询沿着落点弹到出去为止 修改从块开始到这个点为止修改一遍 这样修改和查询都是O(√n)的 #include<cmath> #include<cstdio> #include<cstring>

HNOI2010弹飞绵羊(块状数组)

不得不说块状数组好神奇的啊!这道题的标签可是splay的启发是合并(什么高大上的东西),竟然这么轻松的就解决了! var x,y,i,j,tot,n,m,ch:longint; f,k,l,bl,go:array[0..200100] of longint; procedure init; begin readln(n); x:=trunc(sqrt(n));j:=x; for i:=1 to n do begin if j=x then begin j:=1;inc(tot);l[tot]:=

BZOJ2002 HNOI2010 弹飞绵羊 LCT

LCT=树链剖分+平衡树(多用Splay),AC之后才对这个等式深有感触-- 我们定义一个操作Access(x),这个操作可以将从x到根节点路径上的所有节点放到一颗Splay里,一切操作都在这棵Splay里做.你说要维护任意两个点的路径?反正是无向树换一下根不就好了-- 我们以执行Access的顺序为权值来进行树链剖分,每一条链用一颗Splay按照深度来维护,每一颗Splay与他的父亲(由于一条链一定是在一个点u的子树上,所以我们只需要让这颗Splay的根与u相连就好,无所谓根和u在原树中是否相

洛谷 P3203 [HNOI2010]弹飞绵羊 || bzoj2002

看来这个lct板子的确没什么问题 好像还可以分块做 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 typedef long long LL; 5 namespace LCT 6 { 7 struct Node 8 { 9 Node *ch[2],*fa; 10 bool rev; 11 LL sz; 12 void upd() 13 { 14 sz=(ch[0]?ch[0]->sz:0)+(

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&

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