loj 6279分块入门

94 篇文章 10 订阅
2 篇文章 0 订阅

题目链接

https://loj.ac/problem/6279

题意

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

思路

分块区间修改板题。
分块的时候,就对每一个块进行排序,查找时,边块角块依旧暴力,整块使用 l o w e r _ b o u n d lower\_bound lower_bound u p p e r _ b o u n d upper\_bound upper_bound 二分查找。
注意,边块角块修改后所在块要重新排序。

参考代码

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int n,block,cnt;
int a[100010],addv[400],belong[100010];
vector<int> vc[400];
void Sort() {
	for(int i=1; i<=cnt; i++) {
		sort(vc[i].begin(),vc[i].end());
	}
}
void update(int blockn) {
	vc[blockn].clear();
	for(int i=(blockn-1)*block+1; i<=blockn*block; i++) {
		vc[blockn].push_back(a[i]);
	}
	sort(vc[blockn].begin(),vc[blockn].end());
}
void modify(int l,int r,int add) {
	for(int i=l; i<=min(belong[l]*block,r); i++) {
		a[i]+=add;
	}
	update(belong[l]);
	if(belong[l]!=belong[r]) {
		for(int i=(belong[r]-1)*block+1; i<=r; i++) {
			a[i]+=add;
		}
		update(belong[r]);
	}
	for(int i=belong[l]+1; i<belong[r]; i++) {
		addv[i]+=add;
	}
}
int query(int l,int r,int maxn) {
	int ans=-inf;
	for(int i=l; i<=min(belong[l]*block,r); i++) {
		if(a[i]+addv[belong[i]]<maxn) {
			ans=max(ans,a[i]+addv[belong[i]]);
		}
	}
	if(belong[l]!=belong[r]) {
		for(int i=(belong[r]-1)*block+1; i<=r; i++) {
			if(a[i]+addv[belong[i]]<maxn) {
				ans=max(ans,a[i]+addv[belong[i]]);
			}
		}
	}
	for(int i=belong[l]+1; i<belong[r]; i++) {
		if(vc[i].at(0)+addv[i]>=maxn) {
			continue;
		}
		int k=lower_bound(vc[i].begin(),vc[i].end(),maxn-addv[i])-vc[i].begin();
		ans=max(ans,vc[i].at(k-1)+addv[i]);
	}
	return ans;
}
int main() {
	int n;
	scanf("%d",&n);
	block=sqrt(n);
	for(int i=1; i<=n; i++) {
		scanf("%d",&a[i]);
		belong[i]=(i-1)/block+1;
		vc[belong[i]].push_back(a[i]);
		if(i%block==1) {
			cnt++;
		}
	}
	Sort();
	for(int i=1; i<=n; i++) {
		int op,l,r,c;
		scanf("%d%d%d%d",&op,&l,&r,&c);
		if(op==0) {
			modify(l,r,c);
		} else {
			int k=query(l,r,c);
			if(k==-inf) {
				printf("-1\n");
				continue;
			}
			printf("%d\n",k);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuoyanli

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值