P3203 弹飞绵羊 题解

题目传送门

暴力

此做法十分好想就是先预处理出n-1 ~ 0每个位置弹飞需要多少步与弹到的下一个位置存在两个数组num[]与out[]里,修改就暴力从需修改的位置到1这两个数组。a[]存储的是是劲度系数。

正解

其实想出了暴力就成功了一半了。暴力最大的时间消耗是由于修改花费了o(n)的效率显然不优,所以我们想到优化修改的时效。现在我们就可以请出我的神奇的分块的了。依照分块的基本操作先分块:分块 + 利用暴力的思想来处理出每个位置弹出它所在的块需要多少步与弹出块位置存在两个数组num[]与out[]里

我们先想一想将以下位置i的a[],会产生了什么影响:

是的,对i以后的位置没有影响,只对其前的位置有影响,那么我们只需修改i所在的块且在i之前的位置的out[]与num[]即可。为什么呢?因为在i之前的所有位置只会间接或直接的跳到i或跳过i

这样在i所在块之前的所有的位置在跳入的i所在块之前其的num[]与out[]不会有任何影响。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn], L[maxn], R[maxn], out[maxn], num[maxn], pos[maxn];
int n, m;
int Num;
void Init(){
	for(int i = 1; i <= Num; i++){
		for(int j = R[i]; j >= L[i]; j--){
			if(a[j] + j > R[i]){
				num[j] = 1;
				out[j] = j + a[j];
			}
			else{
				num[j] = num[j + a[j]] + 1;
				out[j] = out[j + a[j]];
			}
			
		}
	}
}
void updata(int x, int k){
	int s = pos[x];
	a[x] = k;
	for(int i = x; i >= L[s]; i--){
		if(a[i] + i > R[s]){
			num[i] = 1;
			out[i] = i + a[i];
		}
		else{
			num[i] = num[i + a[i]] + 1;
			out[i] = out[i + a[i]];
		}
	}
}
void ask(int x){
	int ans = 0, now = x;
	while(now < n){
		ans += num[now];
		now = out[now];
	}
	printf("%d\n", ans);
}
void Read(){
	scanf("%d", &n);
	for(int i = 0; i < n; i++)
		scanf("%d", &a[i]);
	int block = sqrt(n);
	Num = n / block;
	if(n % Num) Num++;
	for(int i = 1; i <= Num; i++){
		L[i] = (i - 1) * block;
		R[i] = i * block - 1;
	}
	R[Num] = n - 1;
	for(int i = 1; i <= Num; i++){
		for(int j = L[i]; j <= R[i]; j++)
			pos[j] = i;
	}
	Init();
	scanf("%d", &m); 
	for(int i = 1; i <= m; i++){
		int opt, x, k;
		scanf("%d%d", &opt, &x);
		if(opt == 1) ask(x);
		else {
			scanf("%d", &k);
			updata(x, k);
		}
	}
}
int main(){
	Read();
	return 0;
}

总结

本题就是用分块思想来优化修改

posted @ 2023-03-09 21:58  lyc2006  阅读(15)  评论(0编辑  收藏  举报