数列分块入门 3

问题描述

给出一个长为 n n n 的数列,以及 n n n 个操作,操作涉及区间加法,询问区间内小于某个值 x x x 的前驱(比其小的最大元素)。

输入格式

第一行输入一个数字 n n n

第二行输入 n n n 个数字,第 i 个数字为 a i a_i ai,以空格隔开。

接下来输入 n n n 行询问,每行输入四个数字 o p t , l , r , c opt,l,r,c opt,l,r,c,以空格隔开。

o p t = 0 opt = 0 opt=0,表示将位于 [ l , r ] [l, r] [l,r] 的之间的数字都加 c c c

o p t = 1 opt = 1 opt=1,表示询问 [ l , r ] [l, r] [l,r] c c c 的前驱的值(不存在则输出 − 1 -1 1)。

输出格式

对于每次询问,输出一行一个数字表示答案。

样例

样例输入1:

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

样例输出1:

3
-1

数据范围

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 100000 1 \le n \le 100000 1n100000 − 2 31 ≤ o t h e r s , a n s ≤ 2 31 − 1 -2^{31}\le others,ans\le2^{31}-1 231others,ans2311

题解

类似数列分块2,只不过一个是求小于 x x x 的个数,一个是求 x x x 的前驱而已。

#include<bits/stdc++.h>
using namespace std;
long long n, kuai_cnt, kuai_len;
long long a[100010], add[410], belong[100010];
vector<long long> v[410];
void reset(long long x){
	v[x].clear();
	for(long long i = (x - 1) * kuai_len + 1; i <= min(x * kuai_len, n); ++ i){
		v[x].push_back(a[i]);
	}
	sort(v[x].begin(), v[x].end());
}
void change(long long l, long long r, long long x){
	for(long long i = l; i <= min(belong[l] * kuai_len, r); ++ i){
		a[i] += x;
	}
	reset(belong[l]);
	if(belong[l] != belong[r]){
		for(long long i = (belong[r] - 1) * kuai_len + 1; i <= r; ++ i){
			a[i] += x;
		}
		reset(belong[r]);
	}
	for(long long i = belong[l] + 1; i <= belong[r] - 1; ++ i){
		add[i] += x;
	}
}
long long query(long long l, long long r, long long x){
	long long sum = -1;
	for(long long i = l; i <= min(r, belong[l] * kuai_len); ++ i){
		if(a[i] + add[belong[l]] < x){
			sum = max(sum, a[i] + add[belong[l]]);
		}
	}
	if(belong[l] != belong[r]){
		for(long long i = (belong[r] - 1) * kuai_len + 1; i <= r; ++ i){
			if(a[i] + add[belong[r]] < x){
				sum = max(sum, a[i] + add[belong[r]]);
			}
		}
	}
	for(long long i = belong[l] + 1; i <= belong[r] - 1; ++ i){
		long long t = x - add[i];
		auto it = upper_bound(v[i].begin(), v[i].end(), t - 1);
		if(it != v[i].begin()){
			-- it;
			long long p = *it + add[i];
			if(p < x) sum = max(sum, p); 
		}
	}
	return sum;
}
int main(){
	scanf("%lld", &n);
	for(long long i = 1; i <= n; ++ i){
		scanf("%lld", &a[i]);
	}
	kuai_len = sqrt(n);
	kuai_cnt = (n + kuai_len - 1) / kuai_len;
	for(long long i = 1; i <= n; ++ i){
		belong[i] = (i - 1) / kuai_len + 1;
		v[belong[i]].push_back(a[i]);
	}
	for(long long i = 1; i <= kuai_cnt; ++ i){
		sort(v[i].begin(), v[i].end());
	}
	for(long long i = 1; i <= n; ++ i){
		long long op, l, r, d;
		scanf("%lld %lld %lld %lld", &op, &l, &r, &d);
		if(op == 0){
			change(l, r, d);
		}
		else{
			printf("%lld\n", query(l, r, d));
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值