luoguP3203. [HNOI2010]弹飞绵羊

部署运行你感兴趣的模型镜像

problem

题目描述

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

输入输出格式

输入格式:
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1。

接下来一行有n个正整数,依次为那n个装置的初始弹力系数。

第三行有一个正整数m,

接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。

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

输入输出样例

输入样例#1: 复制
4
1 2 1 1
3
1 1
2 1 1
1 1
输出样例#1: 复制
2
3
说明

对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000


analysis

  • LCT板子题

  • 我们可以建一个 n+1 n + 1 号节点 x x 号节点连到表示n+1号节点就表示被弹飞

  • 若要更改,直接先 cut c u t link l i n k 一波,关键怎么询问弹飞几次呢?暴力splay?

  • 我们可以 makeroot(n+1) m a k e r o o t ( n + 1 ) ,使 n+1 n + 1 号节点变为原LCT的根

  • 接着我们 access(x) a c c e s s ( x ) splay(x,0) s p l a y ( x , 0 ) 也就是把 x x 旋转到它原来辅助树的根

  • 那么x号节点会弹 xsize1 x s i z e − 1 次被弹飞,但是为什么呢?

  • 由于辅助树按照深度关键字排序所以x的深度-1就是要被弹飞几次了

  • 所以明显正确


code

#include<stdio.h>
#include<string.h>
#define MAXN 200001

using namespace std;

int t[MAXN][2];
int b[MAXN],fa[MAXN],pf[MAXN],st[MAXN];
int n,m;

struct node 
{
    int size,rev;
}a[MAXN];

int min(int x,int y) 
{
    return x<y?x:y;
}

void swap(int &x,int &y)
{
    int z=x;
    x=y;
    y=z;
}

void change(int x) 
{
    if(x)
    {
        a[x].rev^=1;
        swap(t[x][0],t[x][1]);
    }
}

void down(int x) 
{
    if (a[x].rev) 
    {
        change(t[x][0]),change(t[x][1]);
        a[x].rev=0;
    }
}

void update(int x) 
{
    if(x)
    {
        a[x].size=a[t[x][0]].size+a[t[x][1]].size+1;
    } 
}

void downdata(int x) 
{
    while (x)st[++st[0]]=x,x=fa[x]; 
    while (st[0])down(st[st[0]--]);
}

int lr(int x) 
{
    return t[fa[x]][1]==x;
}

void rotate(int x) 
{
    int y=fa[x],k=lr(x);
    t[y][k]=t[x][!k]; 
    if(t[x][!k])fa[t[x][!k]]=y;
    fa[x]=fa[y];
    if(fa[y])t[fa[y]][lr(y)]=x;    
    t[x][!k]=y; 
    fa[y]=x; 
    pf[x]=pf[y];
    update(y),update(x);
}

void splay(int x, int y) 
{
    downdata(x);
    while(fa[x]!=y)
    {
        if(fa[fa[x]]!=y)
        {
            if(lr(x)==lr(fa[x]))rotate(fa[x]); 
            else rotate(x);
        } 
        rotate(x);
    }
}

void access(int x) 
{
    for (int y=0;x;update(x),y=x,x=pf[x])
    {
        splay(x,0);
        fa[t[x][1]]=0;
        pf[t[x][1]]=x;
        t[x][1]=y;
        fa[y]=x;
        pf[y]=0;
    }
}

void makeroot(int x) 
{
    access(x); 
    splay(x,0); 
    change(x);
}

void link(int x, int y) 
{
    makeroot(x); 
    pf[x]=y;
}

void cut(int x, int y) 
{
    makeroot(x); 
    access(y);
    splay(y,0);
    t[y][0]=fa[x]=pf[x]=0;
    update(y);
}

int main() 
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) 
    {
        scanf("%d",&b[i]);
        link(i,min(n+1,i+b[i]));
    }
    scanf("%d",&m);
    while (m--)
    {
        int z,x; 
        scanf("%d%d",&z,&x),x++;
        if (z==1) 
        {
            makeroot(n+1);
            access(x);
            splay(x,0);
            printf("%d\n",a[x].size-1);
        } 
        else 
        {
            cut(x,min(x+b[x],n+1));
            scanf("%d",&b[x]);
            link(x,min(x+b[x],n+1));
        }
    }
}

您可能感兴趣的与本文相关的镜像

Linly-Talker

Linly-Talker

AI应用

Linly-Talker是一款创新的数字人对话系统,它融合了最新的人工智能技术,包括大型语言模型(LLM)、自动语音识别(ASR)、文本到语音转换(TTS)和语音克隆技术

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值