LibreOJ 6278. 数列分块入门 2

本文介绍了一种解决数列区间操作问题的有效方法——数列分块算法,并通过具体实例展示了如何实现区间加法和查询区间内特定条件的元素数量。文章详细解释了算法原理、复杂度分析及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#6278. 数列分块入门 2

内存限制:256 MiB 时间限制:500 ms 标准输入输出
题目类型:传统 评测方式:文本比较
上传者: hzwer
题目描述

给出一个长为 nnn 的数列,以及 nnn 个操作,操作涉及区间加法,询问区间内小于某个值 xxx 的元素个数。

输入格式

第一行输入一个数字 nnn

第二行输入 nnn 个数字,第 i 个数字为 aia_iai,以空格隔开。

接下来输入 nnn 行询问,每行输入四个数字 opt\mathrm{opt}optlllrrrccc,以空格隔开。

若 opt=0\mathrm{opt} = 0opt=0,表示将位于 [l,r][l, r][l,r] 的之间的数字都加 ccc

若 opt=1\mathrm{opt} = 1opt=1,表示询问 [l,r][l, r][l,r] 中,小于 c2c^2c2 的数字的个数。

输出格式

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

样例
样例输入
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
样例输出
3
0
2
数据范围与提示

对于 100% 100\%100% 的数据,1≤n≤50000,−231≤others 1 \leq n \leq 50000, -2^{31} \leq \mathrm{others}1n50000,231othersans≤231−1 \mathrm{ans} \leq 2^{31}-1ans2311



题解:妈的什么情况,老子足足WA了9发才终于改好。气死

orzhzwer

对于每次区间操作:

1.不完整的块 的O(√n)个元素怎么处理?

2.O(√n)个 整块 怎么处理?

3.要预处理什么信息(复杂度不能超过后面的操作)?

 

我们先来思考只有询问操作的情况,不完整的块枚举统计即可;而要在每个整块内寻找小于一个值的元素数,于是我们不得不要求块内元素是有序的,这样就能使用二分法对块内查询,需要预处理时每块做一遍排序,复杂度O(nlogn),每次查询在√n个块内二分,以及暴力2√n个元素,总复杂度O(nlogn + n√nlog√n)。

可以通过均值不等式计算出更优的分块大小,就不展开讨论了

 

那么区间加怎么办呢?

套用第一题的方法,维护一个加法标记,略有区别的地方在于,不完整的块修改后可能会使得该块内数字乱序,所以头尾两个不完整块需要重新排序,复杂度分析略。

在加法标记下的询问操作,块外还是暴力,查询小于(x – 加法标记)的元素个数,块内用(x – 加法标记)作为二分的值即可。



分块真的是什么鬼东西、、、


代码:

#include<bits/stdc++.h>
using namespace std;
struct aaa{
	long long w;
	int v;
}aa[100001];
int n,m,i,j,l,r,t1,ll,rr,mid,ans,t;
long long a[100001],f[100001],b[100001],c;
bool cmp(aaa a,aaa b){
	return a.w<b.w;
}
int main(){
	scanf("%d",&n);
	m=sqrt(n);
	for(i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		aa[i].w=a[i];aa[i].v=i;
		b[i]=(i-1)/m+1;
	}
	for(i=1;i<m;i++){
		sort(aa+(i-1)*m+1,aa+i*m+1,cmp);
	}
	sort(aa+(m-1)*m+1,aa+n+1,cmp);
	for(i=1;i<=n;i++){
		scanf("%d%d%d%lld",&t1,&l,&r,&c);
		if(t1==0){
			if(b[l]==b[r]){
				for(j=(b[l]-1)*m+1;j<l;j++)aa[j].w=a[j];
				for(j=r+1;j<=b[l]*m;j++)aa[j].w=a[j];
			 for(j=l;j<=r;j++){
			 a[j]+=c;
			 aa[j].w=a[j];
		}
		sort(aa+(b[l]-1)*m+1,aa+b[l]*m+1,cmp);
		}
			 else{
			 	for(j=(b[l]-1)*m+1;j<l;j++){
			 		aa[j].w=a[j];
			 		aa[j].v=j;
				 }
				for(j=r+1;j<=b[r]*m;j++){
					aa[j].w=a[j];
					aa[j].v=j;
				} 
			 	for(j=l;j<=b[l]*m;j++){
			 		a[j]+=c;
			 		aa[j].w=a[j];
			 		aa[j].v=j;
				 }
			 	for(j=(b[r]-1)*m+1;j<=r;j++){
				 a[j]+=c;
				 aa[j].w=a[j];
				 aa[j].v=j;
			}
			sort(aa+(b[l]-1)*m+1,aa+b[l]*m+1,cmp);
			sort(aa+(b[r]-1)*m+1,aa+b[r]*m+1,cmp);
			 	for(j=b[l]+1;j<b[r];j++)f[j]+=c;
			 }
		}
		 else{
		 	c*=c;ans=0;
		 	if(b[l]==b[r]){
		 		for(j=l;j<=r;j++)if(f[b[l]]+a[j]<c)ans++;
		 }
		  else{
		  	for(j=l;j<=b[l]*m;j++)
		  	 if(f[b[l]]+a[j]<c)ans++;
		  	for(j=(b[r]-1)*m+1;j<=r;j++)
			   if(f[b[r]]+a[j]<c)ans++;
			for(j=b[l]+1;j<b[r];j++){
				ll=(j-1)*m+1;rr=j*m;
				t=ll-1;
				while(ll<=rr){
					mid=(ll+rr)/2;
					if(aa[mid].w+f[j]<c){
						t=max(t,mid);
						ll=mid+1;
					}
					 else rr=mid-1;
				}
				ans+=t-(j-1)*m;
			}    
		  }
		printf("%d\n",ans);  
	}
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值