bzoj2002Bounce 弹飞绵羊 动态树(Link-Cut-Tree)

本文介绍了一款名为“Bounce弹飞绵羊”的游戏背后的算法实现。游戏中玩家需通过动态调整弹力装置来计算绵羊从起点到终点的路径长度。文章详细解释了如何使用辅助树结构高效地进行节点链接和查询操作。

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

2002: [Hnoi2010]Bounce 弹飞绵羊

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 11830   Solved: 5986
[ Submit][ Status][ Discuss]

Description

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

Input

第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,接下来一行有n个正整数,依次为那n个装置的初始弹力系数。第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000

Output

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

Sample Input

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

Sample Output

2
3

HINT

Source

题目大意:动态修改询问一个点到链尾的距离。

裸的动态树。

其实可以不裸。

考虑修改,Link的时候,我们考虑把点怼到辅助树的根的时候,左边是他的爸爸们,也就是从这个点到链尾的所与节点。这个时候,需要把它和后面的链点断开,所以直接在辅助树上把它和它的左子树断开(记得儿子父亲都要断)。然后重新连链就可以了。酱紫的话,连rev,makeroot,cut,pushdown什么的都不用啦,效率还可以。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define maxn 220000
#define ls t[p].ch[0]
#define rs t[p].ch[1]
#define pa t[p].f
using namespace std;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 - '0' + ch; ch = getchar();}
    return x * f;
}
 
struct node {
    int f, ch[2];
    int s;
}t[maxn]; int n, m;
bool wh(int p) {return t[pa].ch[1] == p;}
bool Isroot(int p) {return t[pa].ch[0] != p && t[pa].ch[1] != p;} 
void update(int p) {t[p].s = t[ls].s + t[rs].s + 1;}
 
void Rotate(int p) {
	int f = pa, g = t[f].f, c = wh(p);
	if(!Isroot(f)) t[g].ch[wh(f)] = p; t[p].f = g;
	t[f].ch[c] = t[p].ch[c ^ 1]; if(t[f].ch[c]) t[t[f].ch[c]].f = f;
	t[p].ch[c ^ 1] = f; t[f].f = p;
	update(f);
}
 
void Splay(int p) {
	for(; !Isroot(p); Rotate(p)) 
		if(!Isroot(pa)) Rotate(wh(p) == wh(pa) ? pa : p);
	update(p);
}
 
void Access(int p) {
	for(int pre = 0; p; pre = p, p = pa) {
		Splay(p);
		t[p].ch[1] = pre;
		update(p);
	}
}
 
void Link(int p, int g) {
	Access(p); Splay(p);
	t[ls].f = 0; ls = 0;
	pa = g; update(p);
}

void Query(int p) {Access(p); Splay(p); printf("%d\n", t[ls].s + 1);}
 
int main()
{
	n = read();
	for(int i = 1;i <= n; ++i) {
		int k = read(); t[i].s = 1;
		if(i + k <= n) Link(i, i + k);
	}
	m = read();
	for(int i = 1;i <= m; ++i) {
		int opt = read();
		if(opt == 1) Query(read() + 1);
		else {
			int x = read() + 1, y = read();
			if(x + y > n) Link(x, 0);
			else Link(x, x + y);
		}
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值