(分块)LOJ#6279. 数列分块入门 3

传送门:LOJ#6279. 数列分块入门 3

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

以下引用hzwer大佬的解答:

n<=100000其实是为了区分暴力和一些常数较大的写法。

接着第二题的解法,其实只要把块内查询的二分稍作修改即可。

不过这题其实想表达:可以在块内维护其它结构使其更具有拓展性,比如放一个 set ,这样如果还有插入、删除元素的操作,会更加的方便。

 

分块的调试检测技巧:

可以生成一些大数据,然后用两份分块大小不同的代码来对拍,还可以根据运行时间尝试调整分块大小,减小常数。

这里我直接用vector来实现的:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=100010;
//const int maxm=sqrt(maxn)+10;
 
ll read(){ //读入挂 
	ll x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}
 
int n,block;
int a[maxn],pos[maxn],tag[maxn];
vector<int>v[510];
 
void  reset(int x){
	v[x].clear();
	for(int i=(x-1)*block+1;i<=min(x*block,n);i++)
		v[x].push_back(a[i]);
	sort(v[x].begin(),v[x].end());
}
 
void add(int l,int r,int c){
	for(int i=l;i<=min(pos[l]*block,r);i++)
		a[i]+=c;
	reset(pos[l]);
	if(pos[l]!=pos[r]){
		for(int i=(pos[r]-1)*block+1;i<=r;i++)
			a[i]+=c;
		reset(pos[r]);
	}
	for(int i=pos[l]+1;i<=pos[r]-1;i++)
		tag[i]+=c;
}
 
int query(int l,int r,int c){
	int ans=-1;
	for(int i=l;i<=min(pos[l]*block,r);i++)
		if(a[i]+tag[pos[l]]<c&&a[i]+tag[pos[l]]>ans) ans=a[i]+tag[pos[l]];
	if(pos[l]!=pos[r]){
		for(int i=(pos[r]-1)*block+1;i<=r;i++)
			if(a[i]+tag[pos[r]]<c&&a[i]+tag[pos[r]]>ans) ans=a[i]+tag[pos[r]];	
	}
	for(int i=pos[l]+1;i<=pos[r]-1;i++){
		int x=c-tag[i];
		int k=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
		if(k-1>=0&&v[i][k-1]+tag[i]>ans) ans=v[i][k-1]+tag[i];
	}
	return ans;
}
 
int main(){
	n=read();
	block=sqrt(n);
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<=n;i++){
		pos[i]=(i-1)/block+1;
		v[pos[i]].push_back(a[i]);
	}
	//cout<<"**"<<endl;;
	for(int i=1;i<=pos[n];i++){
		sort(v[i].begin(),v[i].end());
	}
	int opt,l,r,c;
	for(int i=1;i<=n;i++){
		opt=read();
		l=read();
		r=read();
		c=read();
		if(opt==0) add(l,r,c);
		else printf("%d\n",query(l,r,c));
	}
	return 0;
}
 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值