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
题解:
首先,测试时想到的是修改优化,查询O(1),建虚拟节点,删边,连边,却不知怎么实现,直到
@abcde 讲是LCT裸版题,才。。以后有空再补吧,
言归正传,区间询问,修改应第一反应分块||莫队,且这道题维护的不是树,而是一维区间,且是跳到尾端的次数,很适合分块。
于是 nxt[i] 保存i跳到下一块的某位,cnt[i] 保存i跳出当前块所需的步数
查询时logn跳,修改时暴力修改区间logn

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#define N 200005
#define log_N 505
using namespace std;
int bl[log_N],br[log_N],b_num,nxt[N],cnt[N],n,m,a[N],belong[N];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);

	}
	int leng=(int)sqrt(n*1.0);
	for(int i=1;i<=n;){
		if(i+leng-1>n)break;
		bl[++b_num]=i;br[b_num]=i+leng-1;
		for(int j=bl[b_num];j<=br[b_num];j++){
			belong[j]=b_num;
		}
		i=i+leng;
	}
	//cout<<leng<<endl;
	if(br[b_num]<n){
		bl[b_num+1]=br[b_num]+1;
		br[++b_num]=n;
		for(int i=bl[b_num];i<=br[b_num];i++){
			belong[i]=b_num;
		}
	}
	for(int i=n;i;--i){
		if(i+a[i]>br[belong[i]]){
			cnt[i]=1;nxt[i]=i+a[i];
		}else{
			cnt[i]=cnt[i+a[i]]+1;
			nxt[i]=nxt[i+a[i]];
		}
	}
//	cout<<nxt[1]<<endl;
 //   for(int i=1;i<=n;i++)cout<<nxt[i]<<" ";
	scanf("%d",&m);
//	cout<<m<<endl;
	for(int j=1;j<=m;++j){
	    int op,x,val;
	    scanf("%d",&op);
	    if(op==1){
	    	scanf("%d",&x);
	    	++x;
	    	int ans=0;
	    	while(x<=n){
	    		ans+=cnt[x];	
				//if(nxt[x]<=x)break;
	    		x=nxt[x];
	    	
	    	}//cout<<x<<endl;
	    	printf("%d\n",ans);
	    }else{
	    	scanf("%d%d",&x,&val);
	    	++x;
	    	a[x]=val;
	    	//cout<<br[belong[x]]<<" "<<bl[belong[x]]<<endl;
	    	for(int i=br[belong[x]];i>=bl[belong[x]];--i){
	    		if(i+a[i]>br[belong[i]]){
	    			cnt[i]=1;nxt[i]=i+a[i];
	    		}else{
	    			cnt[i]=cnt[i+a[i]]+1;
	    			nxt[i]=nxt[i+a[i]];
	    		}
	    	}
	    }
	    //cout<<op<<endl;
	}
	
	return 0;
}
	

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值