分块相关知识

目录

0 前导知识

离散化

1 分块入门

1.1 区间最大值,单点修改

1.2 区间加法,单点查询

1.3 区间加法,区间内小于x元素个数

2 莫队

2.1 莫队入门

2.2 回滚莫队

0 前导知识

离散化

一、基本思想

数据离散化是一个非常重要的思想。
为什么要离散化?当以权值为下标的时候,有时候值太大,存不下。 所以把要离散化的每一个数组里面的数映射到另一个值小一点的数组里面去。
打个比方,某个题目告诉你有\(10^4\)个数,每个数大小不超过\(10^{10}\),要你对这些数进行操作,那么肯定不能直接开\(10^{10}\)大小的数组,但是\(10^4\)的范围就完全没问题。
我们来看一下定义:离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。(by百度百科)
通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。例如:
原数据:\(\text1,999,100000,15\);处理后:\(1,3,4,2\)
原数据:{100,200},{20,50000},{1,400};
处理后:{3,4},{2,6},{1,5};
但是离散化仅适用于只关注元素之间的大小关系而不关注元素本身的值!

二、原理与操作

假如你只想简单操作一下,如求个逆序对什么的,那直接排序后将它的顺序覆盖回去就可以啦。(它不能去重)
假如你想写的更加专业就要采用以下步骤:

1.排序
2.去重
3.索引

首先我们要对所要进行离散化的数据进行排序:一般使用sort对数组或结构体排序。
然后是去重操作,为了写出高效的代码,我们需要复习两个STL函数:unique()和lower_bound(),他们同时隶属于#include
unique的作用是“去掉”容器中相邻元素的重复元素(不一定要求数组有序),它会把重复的元素添加到容器末尾(所以数组大小并没有改变),而返回值是去重之后的尾地址;
函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置。【ps.upper_bound是返回第一个大于b[x]的指针,upper_bound()=lower_bound()+1】
这是一种直接的方法:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int lsh[1000], lshcopy[1000], sy[1000]; //lsh[n]是即将被离散化的数组,lshcopy[n]是a[n]的副本,sy[n]用于排序去重后提供离散化后的值
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&sy[i]);
		lshcopy[i]=sy[i];
			
	} 
	sort(sy,sy+n);//第一步排序 
	for(int i=0;i<n;i++)
	{
		cout<<'('<<sy[i]<<')';
		cout<<"\n";
	}
    int size=unique(sy,sy+n)-sy;//unique显示去重后的个数 
    printf("size is : %d",size);
    printf("\n");
    for(int i=0;i<n;i++)
    {
    	lsh[i]=lower_bound(sy,sy+size,lshcopy[i])-sy; //即lsh[i]为lshcopy[i]离散化后对应的值  
		printf("lsh is : %d",lsh[i]);  	
	}
 
}

1 分块入门

1.1 区间最大值,单点修改

用vector数组,对每个块进行排序

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#include <vector>
#define ll long long
int tag[50010],a[50010];
int belong[50010];
int n,B;
vector<int> v[504];
void update(int b){
	v[b].clear();
	for(int i=(b-1)*B+1;belong[i]==b;i++){
		v[b].push_back(a[i]);
	}
	sort(v[b].begin(),v[b].end());
}
void change(int l,int r,int c){
	int bl=belong[l]+1,br=belong[r]-1;
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++){
			a[i]+=c;
		}
		update(belong[l]);
		return ;
	}
	for(int i=bl;i<=br;i++) tag[i]+=c;
	for(int i=l;belong[l]==belong[i];i++) a[i]+=c;
	update(belong[l]);
	for(int i=r;belong[r]==belong[i];i--) a[i]+=c;
	update(belong[r]);
}
int query(int l,int r,int c){
	int res=0;
	int bl=belong[l]+1,br=belong[r]-1;
	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++){
			res+=(a[i]<c-tag[belong[l]]);//?
		}
		return res;
	}
	for(int i=bl;i<=br;i++){
		res+=lower_bound(v[i].begin(),v[i].end(),c-tag[i])-v[i].begin();
	}
	for(int i=l;belong[i]==belong[l];i++){
		res+=(a[i]<c-tag[belong[l]]);
	}
	for(int i=r;belong[i]==belong[r];i--){
		res+=(a[i]<c-tag[belong[r]]);
	}
	return res;
}
int main(){
	scanf("%d",&n);
	B=sqrt(n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		belong[i]=(i-1)/B + 1;
		v[belong[i]].push_back(a[i]);
	}
	int t=n;
	int mb=belong[n];
	for(int i=1;i<=mb;i++){
		sort(v[i].begin(),v[i].end());
	}
	while(t--){
		int w=0,b=0,c=0,d=0;
		scanf("%d%d%d%d",&w,&b,&c,&d);
		if(w==0){
			change(b,c,d);
		}
		else{
			d*=d;
			printf("%d\n",query(b,c,d));
		}
	}
	return 0;
}

1.2 区间加法,单点查询

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
int tag[50010],a[50010],belong[50010];
int n,B;
int query(int q){
	return a[q]+tag[belong[q]];
}
void change(int l,int r,int c){
	int bl=belong[l]+1,br=belong[r]-1;
 	if(belong[l]==belong[r]){
		for(int i=l;i<=r;i++){
			a[i]+=c;
		}		
		//return ;
	}
	else{
		for(int i=bl;i<=br;i++) tag[i]+=c;
		for(int i=l;belong[i]!=bl;i++) a[i]+=c;
		for(int i=r;belong[i]!=br;i--) a[i]+=c;
	}
	
	return ;
}
int main(){
	scanf("%d",&n);
	B=sqrt(n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		belong[i]=(i-1)/B + 1;
	}
	int t=n;
	while(t--){
		int w=0,b=0,c=0,d=0;
		scanf("%d%d%d%d",&w,&b,&c,&d);
		if(w==0){
			change(b,c,d);
		}
		else{
			printf("%d\n",query(c));
		}
	}
	return 0;
}

1.3 区间加法,区间内小于x元素个数

2 莫队

2.1 莫队入门

2.2 回滚莫队

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

posted @ 2020-02-05 11:25  刘子闻  阅读(154)  评论(0编辑  收藏  举报