洛谷P3203 [HNOI2010]弹飞绵羊

题目描述

某天,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。

输出格式:

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

输入输出样例

输入样例#1: 
4
1 2 1 1
3
1 1
2 1 1
1 1
输出样例#1: 
2
3

说明

对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000

LCT板子。。。

首先,建立一个虚拟节点n+1n+1 ,绵羊到达这个节点即被弹飞。

对于每个装置,如果i+Ki<=n ,则执行Link(i,i+Ki) ,否则Link(i,n+1) 。

对于修改操作,先执行Cut(j,j+Kj) (如果j+Kj>n 则为n+1 ),再执行Link(j,j+k) (如果j+k>n 则为n+1n+1 ),并把

Kj 赋为k 。

对于询问操作,分别执行MakeRoot(x) ,Access(n+1) 和Splay(n+1) ,最终答案即为size[n+1]1 。

其中size[i] 表示splay中节点i 的子树的大小。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 200010
using namespace std;
int n,m,w[MAXN];
struct node{
	int son[2];
	int f,s,flag;
}a[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline bool isroot(int rt){
	return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
}
inline void pushup(int rt){
	if(!rt)return;
	a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+1;
}
inline void pushdown(int rt){
	if(!rt||!a[rt].flag)return;
	a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1;
	swap(a[rt].son[0],a[rt].son[1]);
}
inline void turn(int rt){
	int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
	if(!isroot(x)){
		if(a[y].son[0]==x)a[y].son[0]=rt;
		else a[y].son[1]=rt;
	}
	a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
	a[x].son[!k]=a[rt].son[k];a[rt].son[k]=x;
	pushup(x);pushup(rt);
}
void splay(int rt){
	int top=0,stack[MAXN];
	stack[++top]=rt;
	for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
	while(top)pushdown(stack[top--]);
	while(!isroot(rt)){
		int x=a[rt].f,y=a[x].f;
		if(!isroot(x)){
			if((a[x].son[0]==rt)^(a[y].son[0]==x))turn(rt);
			else turn(x);
		}
		turn(rt);
	}
}
void access(int rt){
	for(int i=0;rt;i=rt,rt=a[rt].f){
		splay(rt);
		a[rt].son[1]=i;
		pushup(rt);
	}
}
inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void link(int x,int y){split(x,y);a[x].f=y;}
inline void cut(int x,int y){
	split(x,y);
	if(a[y].son[0]==x)a[x].f=a[y].son[0]=0;
}
int main(){
	int f,x,k;
	n=read();
	for(int i=1;i<=n;i++){
		x=read();
		w[i]=x;
		if(x+i>n)link(i,n+1);
		else link(i,i+x);
	}
	m=read();
	while(m--){
		f=read();x=read()+1;
		if(f==1){
			split(x,n+1);
			printf("%d\n",a[n+1].s-1);
		}
		if(f==2){
			k=read();
			if(x+w[x]>n)cut(x,n+1);
			else cut(x,x+w[x]);
			w[x]=k;
			if(x+w[x]>n)link(x,n+1);
			else link(x,x+w[x]);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值