为了能到远方,脚下的每一步都不能少.|

KeanShi

园龄:1年7个月粉丝:3关注:0

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

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

题意

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

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

n2105m<105

分析

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

题解

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

  • fi 表示从 i 跳几次可以跳到下一个块中,toi 表示从 i 跳到下一个块的位置,firi 记录每个块的开头
  • 修改和查询的时间复杂度均为 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;
}

本文作者:shikean@Tianjin University

本文链接:https://www.cnblogs.com/keanshi/p/17579204.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   KeanShi  阅读(8)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起