【题解】 luogu P3203 [HNOI2010] 弹飞绵羊

文章讲述了如何解决HNOI2010竞赛中的一个问题,即处理大范围数据的跳跃序列。通过分析数据范围和限制,作者提出了使用分块思想而不是单纯的搜索模拟来优化解决方案。代码中展示了如何应用分块策略,以及在块内进行修改和查询的处理,时间复杂度为O(sqrt(n))。此外,还提到了LCT(Link-CutTree)数据结构作为另一种可能的解题方法。
摘要由CSDN通过智能技术生成

题目传送门:P3203 [HNOI2010] 弹飞绵羊

题意

n n n 个数,满足 i < a i ≤ N + 1 i < a_i ≤ N + 1 i<aiN+1 , m m m 次下面两种操作

  1. 修改一个数 a i a_i ai
  2. x x x 开始跳, x = a x x = a_x x=ax, 几次能够跳出序列

n ≤ 2 ∗ 1 0 5 , m < 1 0 5 n \leq 2 * 10^5,m < 10^5 n2105m<105

分析

数据范围比较大,单纯搜索模拟肯定会T,那么考虑每次让他跳一段就停止,然后把每一段跳的次数加起来就是答案;修改也简单,直接在这一段里面使用暴力就行。

题解

可以是LCT板子题,但是还没学LCT,本次采用分块思想:

  • f i f_i fi 表示从 i i i 跳几次可以跳到下一个块中, t o i to_i toi 表示从 i i i 跳到下一个块的位置, f i r i fir_i firi 记录每个块的开头
  • 修改和查询的时间复杂度均为 O ( n ) O(\sqrt n) O(n )

AC代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int k[N], bk[N], fir[N];
int n, m;
int f[N], to[N];

inline void rep(int p, int w) {
    k[p] = w;
    for(int i = fir[bk[p] + 1] - 1; i >= fir[bk[p]]; --i) {
        if(i + k[i] >= fir[bk[i] + 1]) {
            f[i] = 1;
            to[i] = i + k[i];
        } else {
            f[i] = f[i + k[i]] + 1;
            to[i] = to[i + k[i]];
        }
    }
}

inline void query(int p) {
    int ans = 0;
    while(p <= n) {
        ans += f[p];
        p = to[p];
    }
    std::cout << ans << endl;
}

inline void solve() {
    std::cin >> n;
    int bl = sqrt(n);
    for(int i = 1; i <= n; ++i) {
        std::cin >> k[i];
        bk[i] = i % bl == 0 ? i / bl : i / bl + 1;
        if(bk[i] != bk[i - 1]) fir[bk[i]] = i;
    }
    fir[n + 1] = n + 1;
    for(int i = n; i >= 1; --i) {
        if(i + k[i] >= fir[bk[i] + 1]) {
            f[i] = 1;
            to[i] = i + k[i];
        } else {
            f[i] = f[i + k[i]] + 1;
            to[i] = to[i + k[i]];
        }

    }
    std::cin >> m;
    while(m --) {
        int t, p, w;
        std::cin >> t;
        if(t == 1) {
            cin >> p;
            query(p + 1);
        } else {
            cin >> p >> w;
            rep(p + 1, w);
        }
    }
    return;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;
    T = 1;
    while(T --) {
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值