BZOJ P2002 [HNOI2010] 弹飞绵羊【分块】

这道题就很优秀了。考试的时候完全没想到正解,最后写了一个暴力20分…

直接讲分块吧。我们预处理两个数组 S t e p [ ] , G e t [ ] Step[],Get[] Step[],Get[],其中 S t e p [ i ] Step[i] Step[i]表示第 i i i个数跳到下一块需要的步数, G e t [ I ] Get[I] Get[I]表示第 i i i个数跳到下一块中的哪一个数,这样我们就只需要分块处理就可以了。

再讲一下预处理与查询与修改吧。

关于预处理:我们从后往前扫,判断一下当前的这个数与这个数直接跳的数是否处于同一个块中,如果是的话就直接 S t e p [ x ] = S t e p [ x + J u m p [ x ] ] + 1 , G e t [ x ] = G e t [ x + J u m p [ x ] ] Step[x]=Step[x+Jump[x]]+1,Get[x]=Get[x+Jump[x]] Step[x]=Step[x+Jump[x]]+1Get[x]=Get[x+Jump[x]],不然的话就 S t e p [ x ] = 1 , G e t [ x ] = x + J u m p [ x ] Step[x]=1,Get[x]=x+Jump[x] Step[x]=1Get[x]=x+Jump[x]。时间复杂度: O ( n ) O(n) O(n)

关于查询:我们从起点 x x x开始,先将步数记录 A n s + = S t e p [ x ] Ans+=Step[x] Ans+=Step[x],然后再跳过去 x = G e t [ x ] x=Get[x] x=Get[x],就这样循环一直到 G e t [ x ] = 0 Get[x]=0 Get[x]=0。时间复杂度: n \sqrt{n} n

关于修改:修改的话就最开始 J u m p [ x ] = y Jump[x]=y Jump[x]=y,然后我们循环 f o r ( i = x ; i > = L e f t [ B l o c k [ x ] ] ; i − − ) for(i=x;i>=Left[Block[x]];i--) for(i=x;i>=Left[Block[x]];i)预处理操作了。时间复杂度: n \sqrt{n} n

总时间复杂度: O ( n + m n ) O(n+m\sqrt{n}) O(n+mn )

参考代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
using namespace std;
const LL Max=2e5+5;
const LL Mod=1e9+7;
const LL Inf=1e18;
LL N,M,S,SNum,L[Max],R[Max],Get[Max],Step[Max],Jump[Max],Block[Max];
inline LL Read(){
	LL X=0;char CH=getchar();bool F=0;
	while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
	while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
	return F?-X:X;
}
inline void Write(LL X){
	if(X<0)X=-X,putchar('-');
	if(X>9)Write(X/10);
	putchar(X%10+48);
}
void Update(LL X,LL Y){
	LL I;Jump[X]=Y;
	for(I=X;I>=L[Block[X]];I--){
		if(Block[I]==Block[I+Jump[I]]){
			Step[I]=Step[I+Jump[I]]+1,Get[I]=Get[I+Jump[I]];
		} else {
			Step[I]=1,Get[I]=I+Jump[I];
		}
	}
}
LL Calc(LL X){
	LL Ans=0;
	while(true){
		Ans+=Step[X];
		X=Get[X];
		if(X==0){
			return Ans;
		}
	}
}
int main(){
	LL I,J,K;
	N=Read();S=sqrt(N);
	SNum=N/S;
	if(N%S!=0){
		SNum++;
	}
	for(I=1;I<=SNum;I++){
		L[I]=(I-1)*S+1;
		R[I]=I*S;
	}R[SNum]=N;
	for(I=1;I<=N;I++){
		Jump[I]=Read();	
		Block[I]=(I-1)/S+1;
	}
	for(I=N;I;I--){
		if(I+Jump[I]>N){
			Step[I]=1;
		} else if (Block[I]==Block[I+Jump[I]]) {
			Step[I]=Step[I+Jump[I]]+1;Get[I]=Get[I+Jump[I]];
		} else {
			Step[I]=1,Get[I]=I+Jump[I];
		}
	}
	M=Read();
	for(I=1;I<=M;I++){
		K=Read();
		if(K==1){
			LL X=Read()+1;
			Write(Calc(X)),putchar('\n');
		} else {
			LL X=Read()+1,Y=Read();
			Update(X,Y);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值