[HNOI2010]Bounce 弹飞绵羊

本文介绍HNOI2010竞赛中的Bounce题目,玩家需利用弹力装置帮助绵羊跳跃并避免飞出边界。通过LCT树算法实现动态调整弹力值的功能,确保游戏的趣味性和挑战性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[HNOI2010]Bounce 弹飞绵羊
【题目描述】

某天,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。对于10%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
【输出格式】

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

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

【样例输出】

2
3


分析

本蒟蒻并不会分块,但是最近学了LCT到可以巨慢的用LCT做
分析题目
设劲度系数为a[i] 那么i可以向i+a[i]连边。 怎么样才算完成能?我们不难看出如果i+a[i]>n则代表飞出那么就可以设一个节点t=n+1; 如果能飞出的话就向t连边


query(x)

将t移到根节点 再access(x)。那么x与y就有一条路径,这条路径就是弹射的过程。
怎么求解呢?
维护一个size 每次都更改size。 splay(x) T[x].size-1就是答案


modify(x,y)

切掉上一条边,再加一条新边。


代码

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=500000+5;
struct node
{
    int size,fa,ch[2]; bool reverse,is_root;
}T[maxn];
int a[maxn];
int n,m,t;
int getson(int x);
void link(int x,int y);
void cut(int x,int y);
void access(int x);
void splay(int x);
void rotate(int x);
void pushdown(int x);
void push(int x);
void pushreverse(int x);
void mroot(int x);
int query(int x);
int main()
{
    freopen("bzoj_2002.in","r",stdin);
    freopen("bzoj_2002.out","w",stdout);
    scanf("%d",&n);
    t=n+1;
    for(int i=1; i<=n+1; i++)
    {
        T[i].is_root=true; T[i].fa=T[i].ch[0]=T[i].ch[1]=T[i].reverse=0;
    }
    for(int i=1; i<=n; i++)
    {
        int x; scanf("%d",&x);a[i]=x;
        if(i+x>n)
        {
            link(i,t);
        }
        else link(i,x+i);
    }
    scanf("%d",&m);
    for(int i=1; i<=m; i++)
    {
            int opt;
            scanf("%d",&opt);
            if(opt==1)
            {
                int x;
                scanf("%d",&x); x++; printf("%d\n",query(x)-1);
            }
            else
            {
                int x,y;
                scanf("%d %d",&x,&y); x++;
                int aim;
                aim=x+a[x]>n? t:x+a[x];
                cut(x,aim);
                aim=x+y>n? t: x+y; a[x]=y;
                link(x,aim);
            }
    }
    return 0;
}
void update(int x)
{
    T[x].size=T[T[x].ch[1]].size+T[T[x].ch[0]].size+1;
}
int getson(int x)
{
    return T[T[x].fa].ch[1]==x;
}
void pushreverse(int x)
{
    if(!x) return;
    swap(T[x].ch[0],T[x].ch[1]);
    T[x].reverse^=1;
}
void pushdown(int x)
{
    if(T[x].reverse)
    {
        pushreverse(T[x].ch[0]); pushreverse(T[x].ch[1]);
        T[x].reverse=false;
    }
}
void push(int x)
{
    if(!T[x].is_root) push(T[x].fa);
    pushdown(x);
}
void rotate(int x)
{
    int son=getson(x),fa=T[x].fa; int g=T[fa].fa;
    T[fa].ch[son]=T[x].ch[son^1]; T[fa].fa=x; T[x].fa=g;
    if(T[x].ch[son^1]) T[T[x].ch[son^1]].fa=fa; 
    T[x].ch[son^1]=fa;
    if(!T[fa].is_root) T[g].ch[T[g].ch[1]==fa]=x;
    else T[x].is_root=true,T[fa].is_root=false;
    update(fa); update(x);
}
void splay(int x)
{
    push(x);
    for(int fa; !T[x].is_root; rotate(x))
    {
        if(!T[fa=T[x].fa].is_root) rotate(getson(x)==getson(fa)? fa: x);
    }
}
void access(int x)
{
    int y=0;
    do
    {
        splay(x);
        T[T[x].ch[1]].is_root=true;
        T[T[x].ch[1]=y].is_root=false;
        update(x);
        x=T[y=x].fa;
    }while(x);
}
void mroot(int x)
{
    access(x); splay(x); pushreverse(x);
}
void link(int x,int y)
{
    mroot(x); T[x].fa=y;
}
void cut(int x,int y)
{
    mroot(x); access(y); splay(y);
    T[x].is_root=true; T[x].fa=T[y].ch[0]=0;
}
int query(int x)
{
    mroot(t); access(x); splay(x);
    return T[x].size;
}

又臭又长

内容概要:本文档详细介绍了基于MATLAB实现的多头长短期记忆网络(MH-LSTM)结合Transformer编码器进行多变量时间序列预测的项目实例。项目旨在通过融合MH-LSTM对时序动态的细致学习和Transformer对全局依赖的捕捉,显著提升多变量时间序列预测的精度和稳定性。文档涵盖了从项目背景、目标意义、挑战与解决方案、模型架构及代码示例,到具体的应用领域、部署与应用、未来改进方向等方面的全面内容。项目不仅展示了技术实现细节,还提供了从数据预处理、模型构建与训练到性能评估的全流程指导。 适合人群:具备一定编程基础,特别是熟悉MATLAB和深度学习基础知识的研发人员、数据科学家以及从事时间序列预测研究的专业人士。 使用场景及目标:①深入理解MH-LSTM与Transformer结合的多变量时间序列预测模型原理;②掌握MATLAB环境下复杂神经网络的搭建、训练及优化技巧;③应用于金融风险管理、智能电网负荷预测、气象预报、交通流量预测、工业设备健康监测、医疗数据分析、供应链需求预测等多个实际场景,以提高预测精度和决策质量。 阅读建议:此资源不仅适用于希望深入了解多变量时间序列预测技术的读者,也适合希望通过MATLAB实现复杂深度学习模型的开发者。建议读者在学习过程中结合提供的代码示例进行实践操作,并关注模型训练中的关键步骤和超参数调优策略,以便更好地应用于实际项目中。
<think>好的,我现在需要解决用户关于[HNOI2010]绵羊的问题,特别是分块方法的解题思路。首先,我需要回忆一下这个题目的具体内容以及分块算法的一般应用。 题目大意应该是每个装置有一个力系数,当绵羊到达某个位置时会被到后面的某个位置,直到为止。问题应该是要求动态维护所需的步数,或者支持修改某个位置的力系数,同时查询某个点所需的次数。 分块处理这种问题通常是将数据分成多个块,每个块维护一些预处理的信息,这样在查询和修改时可以降低时间复杂度。比如,对于每个位置i,可以预处理出跳出当前块需要的步数,以及跳出后的落点位置。这样查询时就可以逐块跳跃,减少计算量。 接下来,我需要具体思考分块如何在这里应用。每个块的大小通常取√n,这样总共有√n个块。对于每个位置i,在预处理阶段,需要计算从i出发,需要多少步才能跳出当前所在的块,并且记录跳出后的位置。这样,当查询某个点的步数时,只需要累加每个块的步数,直到为止。修改操作时,只需要更新所在块内的信息,因为块内的预处理信息可能被影响,但其他块不受影响。 例如,假设块大小为m,对于每个位置i,如果i + a[i]超出了当前块的范围,则step[i] = 1,next[i] = i + a[i]。否则,step[i] = step[i + a[i]] + 1,next[i] = next[i + a[i]]。这样预处理之后,查询时从当前位置开始,每次跳到next[i],并累加step[i],直到next[i]超过n,即。 修改操作时,比如修改位置k的力系数,那么需要从k所在块的起始位置开始,重新计算该块内所有位置的step和next。这是因为修改可能影响该块内其他位置的预处理结果。比如,如果某个位置j的next[j]原本指向k的位置,修改k的力系数会影响j的next和step,所以需要重新计算整个块的信息。 这样,每次查询的时间复杂度是O(√n),因为每个块最多跳一次,而块的数量是√n。修改操作的时间复杂度是O(√n),因为需要重新处理整个块,大小是√n。 需要注意的是,分块的具体实现需要确定块的大小,通常取√n,但有时根据实际情况调整可能会有更好的效果。另外,预处理每个块的step和next时,需要从块的末尾向前处理,因为后面的位置的处理结果可能影响前面的位置。 可能还需要考虑边界条件,比如力系数是否可能超过数组长度,或者的条件。比如,当i + a[i] >= n时,就算,此时step[i]=1,next[i]=n或某个超出范围的标记。 另外,在实现过程中,如何处理块的分界点,以及如何遍历每个块内的元素,都是需要注意的细节。例如,块的范围是左闭右开还是左闭右闭,需要统一处理,避免数组越界或者漏掉元素。 总结一下,解题步骤应该是: 1. 将整个数组分成若干块,块的大小约为√n。 2. 预处理每个块内的每个位置i的step[i]和next[i],处理顺序应该是从块的最后一个元素向前处理。 3. 查询时,从查询点开始,不断跳到next[i],并累加step[i],直到i超出数组范围,总步数即为答案。 4. 修改时,找到修改位置所在的块,重新预处理该块内的所有元素的step和next。 这样,分块方法能够将时间复杂度降到O(m√n),其中m是操作次数,适用于较大的数据规模,符合算法竞赛的要求。 在代码实现时,需要注意块的下标处理,比如块号从0开始还是1开始,块的大小是否需要调整以适应数组长度不能被整除的情况。此外,预处理函数需要正确计算每个块的起点和终点,并在块内逆序处理元素,以确保后面的元素先被处理,前面的元素可以正确引用后面的step和next值。 举个例子,假设数组长度n=10,块大小m=3,那么块0是0-2,块1是3-5,块2是6-8,块3是9。对于每个块内的元素,比如块0,从位置2开始处理到0。对于位置i=2,如果a[2]=4,则i+a[i]=6,超过块0的范围,所以step[2]=1,next[2]=6。然后处理i=1,假设a[1]=2,则i+a[i]=3,属于块1,所以step[1]=1,next[1]=3。接着处理i=0,假设a[0]=1,则i+a[i]=1,仍在块0内,所以step[0] = step[1] + 1 = 2,next[0] = next[1] =3。这样,当查询位置0时,会先跳到3,再跳到next[3]的位置,依此类推,直到。 因此,分块方法的关键在于预处理每个块内的跳跃信息,使得查询时可以快速跨块计算,而修改时只需要更新所在块的信息,从而将时间复杂度降低到可接受的范围。</think>### [HNOI2010]绵羊分块解法思路 #### 问题分析 题目要求维护一个包含$n$个簧的序列,每个簧有力系数$k_i$。当绵羊落在位置$i$时,会被到$i + k_i$的位置,直到(即位置$\geq n$)。需要支持两种操作: 1. **查询**:从位置$x$出发,多少次会被? 2. **修改**:将位置$x$的力系数改为$y$。 #### 分块思想 将序列分为$\sqrt{n}$个块,每个块大小约为$\sqrt{n}$。预处理每个位置的**跳跃步数**和**落点**,使得: - **块内跳跃信息**:对于每个位置$i$,记录跳出当前块所需的步数$step[i]$和最终落点$next[i]$。 - **查询优化**:每次查询只需逐块跳跃,时间复杂度$O(\sqrt{n})$。 - **修改优化**:修改仅影响当前块的信息,时间复杂度$O(\sqrt{n})$。 #### 预处理方法 1. **块划分**:将数组划分为$m = \lceil \sqrt{n} \rceil$个块,每个块大小为$m$。 2. **逆序处理**:从每个块的最后一个位置向前遍历: - 若$i + k_i$超出当前块,则$step[i] = 1$,$next[i] = i + k_i$。 - 若未超出,则继承下一个位置的步数和落点: $$step[i] = step[i + k_i] + 1, \quad next[i] = next[i + k_i]$$ #### 查询操作 从位置$x$开始,不断跳转到$next[x]$并累加$step[x]$,直到$next[x] \geq n$。总步数即为答案。 #### 修改操作 1. 找到位置$x$所在的块。 2. **重置块内信息**:从该块的最后一个位置重新逆序计算$step$和$next$。 #### 代码框架(Python示例) ```python import math class BlockSolution: def __init__(self, n, k): self.n = n self.k = k.copy() self.block_size = int(math.sqrt(n)) + 1 self.step = [0] * n self.next = [0] * n self.build_blocks() def build_blocks(self): # 分块预处理 for block_start in range(0, self.n, self.block_size): block_end = min(block_start + self.block_size, self.n) for i in range(block_end - 1, block_start - 1, -1): if i + self.k[i] >= block_end: # 跳出当前块 self.step[i] = 1 self.next[i] = i + self.k[i] else: # 依赖块内后续位置 self.step[i] = self.step[i + self.k[i]] + 1 self.next[i] = self.next[i + self.k[i]] def query(self, x): # 查询跳次数 res = 0 while x < self.n: res += self.step[x] x = self.next[x] return res def update(self, x, y): # 修改力系数并重建块 self.k[x] = y block_start = (x // self.block_size) * self.block_size block_end = min(block_start + self.block_size, self.n) for i in range(block_end - 1, block_start - 1, -1): if i + self.k[i] >= block_end: self.step[i] = 1 self.next[i] = i + self.k[i] else: self.step[i] = self.step[i + self.k[i]] + 1 self.next[i] = self.next[i + self.k[i]] ``` #### 复杂度分析 - **预处理**:$O(n)$ - **查询**:$O(\sqrt{n})$ - **修改**:$O(\sqrt{n})$ #### 应用场景 分块法适用于需要**动态维护跳跃路径**且**支持快速修改**的问题,例如绵羊、树状路径跳跃等[^1]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值