#2002. [Hnoi2010]Bounce 弹飞绵羊
Description
某天, L o s t m o n k e y Lostmonkey Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始, L o s t m o n k e y Lostmonkey Lostmonkey在地上沿着一条直线摆上 n n n个装置,每个装置设定初始弹力系数 k i k_i ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第 i + k i i+k_i i+ki个装置,若不存在第 i + k i i+k_i i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第 i i i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣, L o s t m o n k e y Lostmonkey Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
Input
第一行包含一个整数 n n n,表示地上有 n n n个装置,装置的编号从 0 0 0到 n − 1 n-1 n−1,接下来一行有 n n n个正整数,依次为那 n n n个装置的初始弹力系数。第三行有一个正整数 m m m,接下来 m m m行每行至少有两个数 i 、 j i、j i、j,若 i = 1 i=1 i=1,你要输出从 j j j出发被弹几次后被弹飞,若 i = 2 i=2 i=2则还会再输入一个正整数 k k k,表示第 j j j个弹力装置的系数被修改成 k k k。对于 20 % 20\% 20%的数据 n , m ≤ 10000 n,m\leq 10000 n,m≤10000,对于 100 % 100\% 100%的数据 n ≤ 200000 , m ≤ 100000 n\leq 200000,m\leq100000 n≤200000,m≤100000
Output
对于每个 i = 1 i=1 i=1的情况,你都要输出一个需要的步数,占一行。
Sample Input
4
1 2 1 1
3
1 1
2 1 1
1 1
Sample Output
2
3
题意
- 就是给你一个数组 a a a,假如你在一个位置 i i i被弹起来,那么你将落到 i + a [ i ] i+a[i] i+a[i]点处,两种操作,一种是修改 a [ i ] a[i] a[i],另一种是查询从位置 x x x开始跳跳几次后位置第一次 > n >n >n
题解
- 显然 L C T LCT LCT做法无脑暴力,直接 i i i与 m i n ( i + a [ i ] , n + 1 ) min(i+a[i],n+1) min(i+a[i],n+1)连边就行了,查询的时候 s p l i t ( x , n + 1 ) split(x,n+1) split(x,n+1),这条实链的 s i z e − 1 size-1 size−1就是答案
- 网上有大佬用分块做的,留坑待填
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
#define inf 0x3f3f3f3f
namespace LCT{
int ch[maxn][2],fa[maxn],mark[maxn];
int val[maxn],siz[maxn];
inline bool not_root(int x) {return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
inline int dir(int x) {return ch[fa[x]][1]==x;}
inline void add_mark(int x) {swap(ch[x][0],ch[x][1]);mark[x]^=1;} //将x这颗子树翻转
inline void push_down(int x) {
if(mark[x]) {
if(ch[x][0]) add_mark(ch[x][0]);
if(ch[x][1]) add_mark(ch[x][1]);
mark[x]=0;
}
}
inline void push_up(int x) {
siz[x]=1;
if(ch[x][0]) siz[x]+=siz[ch[x][0]];
if(ch[x][1]) siz[x]+=siz[ch[x][1]];
}
inline void pushall(int x) {
if(not_root(x)) pushall(fa[x]);
push_down(x);
}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=dir(x);
if(ch[x][k^1]) fa[ch[x][k^1]]=y;ch[y][k]=ch[x][k^1];
if(not_root(y)) ch[z][dir(y)]=x;fa[x]=z;
ch[x][k^1]=y;fa[y]=x;
push_up(y);
}
inline void splay(int x,int goal=0) {
pushall(x);
while(not_root(x)) {
int y=fa[x],z=fa[y];
if(not_root(y)) {
if(dir(x)==dir(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
push_up(x);
}
inline void access(int x) { //从原树的根向x拉一条实链
for(int y=0;x;y=x,x=fa[x]) {
splay(x);ch[x][1]=y;push_up(x);
}
}
inline void make_root(int x) { //使x成为原树的根
access(x);splay(x);add_mark(x);
}
inline int find_root(int x) { //找到x在原树中的根
access(x);splay(x);
while(ch[x][0]) push_down(x),x=ch[x][0];
splay(x);
return x;
}
inline void split(int x,int y) { //拉出一条x->y的实链,y为splay根
make_root(x);access(y);splay(y);
}
inline bool link(int x,int y) { //连接x与y,若已经在同一颗原树中,返回0
make_root(x);
if(find_root(y)==x) return 0;
fa[x]=y;return 1;
}
inline bool cut(int x,int y) {
make_root(x);
if(find_root(y)!=x||fa[y]!=x||ch[y][0]) return 0;
fa[y]=ch[x][1]=0;
push_up(x);
return 1;
}
};
using namespace LCT;
int n,m,opt,a[maxn],x,y;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
if(i+a[i]<=n) link(i,i+a[i]);
else link(i,n+1);
}
scanf("%d",&m);
for(int i=1;i<=m;i++) {
scanf("%d",&opt);
if(opt==1) {
scanf("%d",&x);x++;
split(x,n+1);
printf("%d\n",siz[n+1]-1);
}else {
scanf("%d %d",&x,&y);
x++;
if(x+a[x]<=n) cut(x,x+a[x]);
else cut(x,n+1);
a[x]=y;
if(x+a[x]<=n) link(x,x+a[x]);
else link(x,n+1);
}
}
}
~~