第一章 基础算法

目录

一、排序

1.1 快速排序

​​​​​​​​​​​1.1.1 题目

1.1.2 代码

1.2 归并排序 

​​​​1.2.1 题目

1.2.2 代码

1.3 时间复杂度

二、二分

2.1 整数二分​​​​​​​

2.1.1 题目

2.1.2 代码

2.2 浮点数二分

三、高精度

3.1 高精度加法

3.1.1 题目

3.1.2 代码

3.2 高精度减法 

3.2.1 题目

3.2.2 代码 

3.3 高精度乘法

3.3.1 题目

3.3.2 代码

3.4 高精度除法 

3.4.1 题目

3.4.2 代码 

四、前缀和

4.1 一维前缀和

4.1.1 题目

4.1.2 代码

4.2 二维前缀和 

4.2.1 题目

4.2.2 代码

五、差分

5.1 一维差分 

5.1.1 题目

5.1.2 代码

5.2 二维差分 

5.2.1 题目

5.2.2 代码

六、双指针算法

6.1 题目:最长连续不重复子序列

6.2 代码

七、位运算

7.1 lowbit 运算 

7.2 题目:二进制中1的个数

7.3 代码

八、离散化

8.1 题目:区间和

8.2 代码

九、区间合并

9.1 题目:区间合并

9.2 代码


一、排序​​​​​​​

​​​​​​​快速排序与归并排序已经是时间复杂度最优的基于比较的排序算法。

1.1 快速排序

分治法把一个问题划分为若干个规模更小的同类子问题,对这些子问题递归求解,然后在回溯时通过它们推导出原问题的解。

每次分治(递归之后),问题规模均会缩小一半。

​​​​​​​​​​​1.1.1 题目

给定你一个长度为n的整数数列。请你便用快速排序对这个数列按照从小到大进行排序。并将排好序的数列按顺字输出。

输入格式

输入共两行,第一行包含整数n。第二行包含n个整数(所有整数均在1~10^9范围内),表整个数列。

输出格式

输出共一行,包含n个整数,表示排好序的数列。

数据范围:1 <= n <= 1000000

输入样例:5        3 1 2 4 5

输出样例:1 2 3 4 5

1.1.2 代码

#include <iostream>
using namespace std;
const int N = 1e6+10;
int n;
int q[N];

void quick_sort(int q[],int l,int r){
	//判边界,一个数或没有数直接退出
	if(l >= r)
		return;
	int x = q[l];
	int i = l-1,j = r+1; //设置两个指针 
	while(i < j){ //迭代 
		do i++;	while(q[i] < x);
		do j--;	while(q[j] > x);
		if(i < j)	swap(q[i],q[j]);
	}
	//用j来设置边界,不能设置x=q[r] 
	quick_sort(q,l,j); 
	quick_sort(q,j+1,r); 
	//用i来设置边界,则应设置x=q[r]或者q[(l+r+1)/2]
	//否者会有边界问题,出现死循环 
	//quick_sort(q,l,i-1);
	//quick_sort(q,i,r);
} 

int main(){
	scanf("%d",&n);
	for(int i=0;i< n;i ++)
		scanf("%d",&q[i]);
	quick_sort(q,0,n-1);
	for(int i=0;i < n;i ++)
		printf("%d ",q[i]);
	return 0;
}

1.2 归并排序 

​​​​1.2.1 题目

给定你一个长度为n的整数数列。请你便用归并排序对这个数列按照从小到大进行排序。并将排好序的数列按顺字输出。

输入格式

输入共两行,第一行包含整数n。第二行包含n个整数(所有整数均在1~10^9范围内),表整个数列。

输出格式

输出共一行,包含n个整数,表示排好序的数列。

数据范围:1 <= n <= 1000000

输入样例:5        3 1 2 4 5

输出样例:1 2 3 4 5

1.2.2 代码

#include <iostream>
using namespace std;

const int N = 1e6+10;
int n;
int q[N];
int tmp[N]; //设置一个辅助数组,用以存储最终结果 

void merge_sort(int q[],int l,int r){
	if(l >= r)
		return;
	int mid = l+r >> 1; //确定分界点,即中点 
	merge_sort(q,l,mid);
	merge_sort(q,mid+1,r);
	int k = 0; //记录结果数组中的个数
	int i = l,j = mid+1; //数组边界指针
	while(i <= mid && j <= r) //两边有序数组非空情况下 
		if(q[i] <= q[j])
			tmp[k++] = q[i++];
		else
			tmp[k++] = q[j++];
	while(i <= mid)
		tmp[k++] = q[i++]; //将左边有序数组剩余元素存入结果数组中 
	while(j <= r)
		tmp[k++] = q[j++]; //将右边有序数组剩余元素存入结果数组中 
	for(i=l,j=0;i <= r;i++,j++)
		q[i] = tmp[j]; //从结果数组中复制结果数据,并存入q 
}

int main(){
	scanf("%d",&n);
	for(int i=0;i < n;i ++)
		scanf("%d",&q[i]);
	merge_sort(q,0,n-1);
	for(int i=0;i < n;i ++)
		printf("%d ",q[i]);
	return 0;
} 

1.3 时间复杂度

快速排序和归并排序的时间复杂度相同,均为 O ( nlogn )。

二、二分

二分的基础的用法是在单调序列或单调函数中进行查找。

2.1 整数二分

对于整数域上的二分,需要注意终止边界、左右区间取舍时开闭情况。避免漏掉答案或造成死循环。

二分写法可能会有两种形式。
1.缩小范围时,r = mid,l = mid+1,取中间值时,mid = (l+r) >>1。
2.缩小范围时,l = mid,r = mid-1,取中间值时,mid = (l+r+1) >>1。
如果不对mid的取法加以区分,假如第二段代码也采用mid=(l+r)>>1,那么当 r-l 等于1时,就有mid=(l+r)>>1 = l。接下来若进入 l=mid 分支,可行区间未缩小,造成死循环;若进入r=mid-1 分支,造成 l > r,循环不能以 l=r 结束。

注意,我们在二分实现中采用了右移运算 >>1,而不是整数除法 /2。这是因为右移运算是向下取整,而整数除法是向零取整,在二分值域包含负数时后者不能正常工作。 

2.1.1 题目

给定一个按照升序排列的长度为n的整数数组,以及q个查询。对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)如果数组中不存在该元素。则返回 “-1-1”。

输入格式

第一行包含整数n和q。表示数组长度和询问个数。第二行包含n个整敌(均在1-10000范围内),表示完整数组。接下来q行,每行包含—个整数k,表示—个询问元素。

输出格式

共q行,每行包含两个整数。表示所求元素的起始位置和终止位置。如果数组中不存在该元素,则返回 “-1-1”。

数据范围:1 <= n <= 100000,1 <= q <= 10000,1 <= k <= 10000。

输入样例:6 3        1 2 2 3 3 4        3        4        5

输出样例:3 4        5 5        -1 -1

2.1.2 代码

#include <iostream>
using namespace std;

const int N = 100010;
int n,m;
int q[N];

int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i < n;i ++)
		scanf("%d",&q[i]);
	while(m --){
		int x;
		scanf("%d",&x);
		int l = 0,r = n-1; 
		//二分求起始坐标 
		while(l < r){
			int mid = l+r >> 1;
			if(q[mid] >= x)
				r = mid;
			else
				l = mid + 1;
		}
		if(q[l] != x) //无解的情况 
			cout << "-1 -1" << endl; 
		else{
			cout << l << " "; //l==r,也可输出r
			l = 0,r = n-1;
			//二分求有边界坐标 
			while(l < r){
				int mid = l+r+1 >> 1;
				if(q[mid] <= x)
					l = mid;
				else
					r = mid - 1;
			} 
			cout << r << endl;
		}
	}
	return 0;	
}

2.2 浮点数二分

对于实数域上的二分,需要注意精度问题。

#include <iostream>
using namespace std;

int main(){
	double x;
	cin >> x;
	double l = 0,r = x;
	while(r-l > 1e-8){ //决定结果的精度
	//for(int i=0;i < 100;i ++) 
		double mid = (l+r)/2;
		if(mid*mid >= x)
			r = mid;
		else	l = mid;
	}
	printf("%lf\n",l);
	return 0;
}

三、高精度

3.1 高精度加法

3.1.1 题目

给定两个正整数,计算它们的和。

输入格式:共两行,每行包含一个整数。

输出格式:共一行,包含所求的和。

数据范围:1 <= 整数长度 <= 1000000。

3.1.2 代码

#include <iostream>
#include <vector>
using namespace std;

//加“&”表示引用,为了提高效率 
vector<int> add(vector<int> &A,vector<int> &B){
	vector<int> C;
	int t = 0;
	for(int i=0;i<A.size() || i<B.size();i ++){
		//t为上次运算的进位 
		if(i < A.size())
			t += A[i];
		if(i < B.size())
			t += B[i];
		C.push_back(t%10);
		t /= 10; //进位到下一位
	}
	if(t) //最后的进位 
		C.push_back(t);
	return C; 
}

int main(){
	string a,b; //由于位数太长,故用字符串读入 
	//vector是向量类型,可容纳许多类型的数据,也被称为容器 
	cin >> a >> b;
	vector<int> A,B; 
	for(int i=a.size()-1;i >= 0;i --)
		A.push_back(a[i]-'0'); //字符串转成数字,倒序存入动态数组中 
	for(int i=b.size()-1;i >= 0;i --)
		B.push_back(b[i]-'0'); 
	vector<int> C = add(A,B);
	for(int i=C.size()-1;i >= 0;i --)
		printf("%d",C[i]); //倒序输出最终结果 
	return 0; 
}

3.2 高精度减法 

3.2.1 题目

给定两个正整数,计算它们的差。

输入格式:共两行,每行包含一个整数。

输出格式:共一行,包含所求的差。

数据范围:1 <= 整数长度 <= 1000000。

3.2.2 代码 

#include <iostream>
#include <vector>
using namespace std;

//判断是否有 A>=B
bool cmp(vector<int> &A,vector<int> &B){
	if(A.size() != B.size()) //先比较位数 
		return A.size() > B.size();
	for(int i=A.size()-1;i >= 0;i --)
		if(A[i] != B[i]) //比较大小 
			return A[i] > B[i];
	return true; //A==B时返回 true 
} 

//A一定大于等于 B 
vector<int> sub(vector<int> &A,vector<int> &B){
	vector<int> C;
	for(int i=0,t=0;i < A.size();i ++){
		t = A[i]-t; //处理借位问题
		if(i < B.size())
			t -= B[i];
		C.push_back((t+10)%10); //t的两种情况合二为一 
		if(t < 0)
			t = 1; //表明有借位 
		else
			t = 0; 
	}
	while(C.size()>1 && C.back()==0)
		C.pop_back(); //去掉前导零 
	return C; 
} 

int main(){
	string a,b;
	vector<int> A,B;
	cin >> a >> b;
	for(int i=a.size()-1;i >= 0;i --)
		A.push_back(a[i]-'0');
	for(int i=b.size()-1;i >= 0;i --)
		B.push_back(b[i]-'0');
	if(cmp(A,B)){
		vector<int> C = sub(A,B);
		for(int i=C.size()-1;i >= 0;i --)
			printf("%d",C[i]);
	}
	else{
		vector<int> C = sub(B,A);
		printf("-");
		for(int i=C.size()-1;i >= 0;i --)
			printf("%d",C[i]);
	} 
	return 0;
} 

3.3 高精度乘法

3.3.1 题目

给定两个正整数A和B,请你计算A*B的值。

输入格式:共两行,第一行包含整数A,第二行包含整数B。

输出格式:共一行,包含A*B的值。

数据范图:1 <= A的长度 <= 1000000,1 <= B <= 10000。

3.3.2 代码

#include <iostream>
#include <vector>
using namespace std;

vector<int> mul(vector<int> &A,int b){
	vector<int> C;
	int t = 0;
	for(int i=0;i<A.size()||t;i ++){ //已经倒序 
		if(i < A.size())
			t += A[i] * b; 
		C.push_back(t%10);
		t /= 10;
	}
	return C;
} 

int main(){
	string a;
	int b;
	cin >> a >> b;
	vector<int> A;
	for(int i=a.size()-1;i >= 0;i --)
		A.push_back(a[i]-'0'); //倒序存入动态数组中 
	vector<int> C = mul(A,b);
	for(int i=C.size()-1;i >= 0;i --)
		printf("%d",C[i]);	
	return 0;
} 

3.4 高精度除法 

3.4.1 题目

给定两个正整数A和B,请你计算A / B的值。

输入格式:共两行,第一行包含整数A,第二行包含整数B。

输出格式:共一行,包含A / B的值。

数据范图:1 <= A的长度 <= 1000000,1 <= B <= 10000。

3.4.2 代码 

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

vector<int> div(vector<int> &A,int b,int &r){ //引用  
	vector<int> C;
	r = 0;
	for(int i=A.size()-1;i >= 0;i --){ //从最高位开始计算 
		r = r*10 + A[i];
		C.push_back(r/b); //C[0]存的最高位 
		r %= b;
	}
	//反转数组 
	reverse(C.begin(),C.end()); //在algorithm头文件中 
	while(C.size()>1 && C.back()==0)
		C.pop_back(); //去掉前导零 
	return C; 
}
 
int main(){
	string a;
	int b;
	cin >> a >> b;
	vector<int> A;
	for(int i=a.size()-1;i >= 0;i --)
		A.push_back(a[i]-'0'); //倒序存入动态数组中 
	int r; //余数 
	vector<int> C = div(A,b,r);
	for(int i=C.size()-1;i >= 0;i --)
		printf("%d",C[i]);
	cout << " " << r << endl; 
	return 0;
} 


四、前缀和

对于一个给定的数列A,它的前缀和数列S是通过递推能求出的基本信息之一。

一个部分和,即数列A某个下标区间内的数的和,可表示为前缀和相减的形式。 

4.1 一维前缀和

4.1.1 题目

输入一个长度为 n 的整数序列,接下来再输入 m 个询问,每个询问输入一对 l 、r,对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。

输入格式

第一行包含两个整数 n 和 m,第二行包含 n 个整数,表示整数序列,接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。

输出格式

共 m 行,每行输出一个询问的结果。

数据范围

1 <= l <= r <= n,1 <=n,m <= 100000,-1000 <= 数列中元素的值 <= 1000。

4.1.2 代码

#include <iostream>
using namespace std;

const int N = 100010;
int n,m;
int a[N],s[N];

int main(){
	ios::sync_with_stdio(false); //提高cin的数据读取速度 
	scanf("%d%d",&n,&m);
	for(int i=1;i <= n;i ++)
		scanf("%d",&a[i]);
	for(int i=1;i <= n;i ++)
		s[i] = s[i-1]+a[i]; //前缀和的初始化 
	while(m --){
		int l,r;
		scanf("%d%d",&l,&r);
		printf("%d\n",s[r]-s[l-1]);
	}
	return 0;
}

4.2 二维前缀和 

4.2.1 题目

输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1、y1、x2、y2,表示一个矩阵的左上角坐标和右下角坐标,对于每个询问输出子矩阵中所有数的和。

输入格式

第一行包含三个整数 n、m、q,接下来 n 行,每行包含 m 个整数,表示整数矩阵,接下来 q 行,每行包含四个整数 x1、y1、x2、y2,表示一组询问。

输出格式

共 q 行,每行输出一个询问的结果。

数据范围

1 <= n、m <= 1000,1 <= q <= 100000,1 <= x1、x2 <= n,1 <= y1、y2 <= m,-1000 <= 矩阵内元素的值 <=1000。

4.2.2 代码

#include <iostream>
using namespace std;

int n,m,q;
int a[N][N],s[N][N];
int main(){
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i <= n;i ++)
		for(int j=1;j <= m;j ++)
			scanf("%d",a[i][j]);
	for(int i=1;i <= n;i ++)
		for(int j=1;j <= m;j ++) //求前缀和 
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
	while(q --){
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		//计算并输出子矩阵的和 
		printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][x2-1]);
	}
	return 0;	
} 

五、差分

对于一个给定的数列A,它的差分数列B定义为:

B[1] = A[1],B[ i ] = A[ i ] - A[ i - 1 ](2 <= i <= n) 

5.1 一维差分 

5.1.1 题目

输入一个长度为 n 的整数序列,接下来输入 m 个操作,每个操作包含三个整数 l、r、c,表示将序列中 [ l ,r ] 之间的每个数加上 c,请你输出进行完所有操作后的序列。

输入格式

第一行包含两个整数 n 和 m,第二行包含 n 个整数,表示整数序列,接下来 m 行,每行包含两个整数 l 、 r、c,表示一个操作。

输出格式

共一行,包含 n 个整数,表示最终序列。

数据范围

1 <= n、m <= 1000,1 <= l <= r <= n,-1000 <= c <= 1000,-1000 <= 整数序列中元素的值 <=1000。

5.1.2 代码

#include <iostream>
using namespace std;

const int N = 100010;
int n,m;
int a[N],b[N]; //在main函数外被初始化为0 

void insert(int l,int r,int c){
	b[l] += c;
	b[r+1] -= c;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i <= n;i ++)
		scanf("%d",&a[i]);
	for(int i=1;i <= n;i ++)
		insert(i,i,a[i]); //利用插入函数实现差分数组
	while(m --){
		int l,r,c;
		scanf("%d%d%d",&l,&r,&c); 
		insert(l,r,c);
	}
	for(int i=1;i <= n;i ++)
	//用差分数组变成自己的前缀和数组,不再另外开辟数组空间 
		b[i] += b[i-1]; 
	for(int i=1;i <= n;i ++)
		printf("%d ",b[i]);
	return 0;
}

5.2 二维差分 

5.2.1 题目

输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1、y1、x2、y2、c,其中 ( x1,y1 ) 和 ( x2,y2)表示一个子矩阵的左上角坐标和右下角坐标,每个操作都要将选中的子矩阵中每个元素的值加上 c,请你将进行完所有操作后的矩阵输出。

输入格式

第一行包含三个整数 n、m、q,接下来 n 行,每行包含 m 个整数,表示整数矩阵,接下来 q 行,每行包含 5 个整数 x1、y1、x2、y2,c,表示一个操作。

输出格式

共 n 行,每行 m 行整数,表示所有操作进行完毕后的最终矩阵。

数据范围

1 <= n、m <= 1000,1 <= q <= 100000,1 <= x1 < x2 <= n,1 <= y1 < y2 <= m,-1000 <= c <= 1000,-1000 <= 矩阵内元素的值 <=1000。

5.2.2 代码

#include <iostream>
using namespace std;

const int N = 1010;
int n,m,q;
int a[N][N],b[N][N];

void insert(int x1,int y1,int x2,int y2,int c){
	b[x1][y1] += c;
	b[x2+1][y1] -= c;
	b[x1][y2+1] -= c;
	b[x2+1][y2+1] += c;
}

int main(){
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i <= n;i ++)
		for(int j=1;j <= m;j ++)
			scanf("%d",&a[i][j]);
	for(int i=1;i <= n;i ++)
		for(int j=1;j <= m;j ++)
		//相当于在单位为1的矩阵中插入一个数据 
			insert(i,j,i,j,a[i][j]);
	while(q--){
		int x1,y1,x2,y2,c;
		cin >> x1 >> y1 >> x2 >> y2 >> c;
		insert(x1,y1,x2,y2,c);
	}
	for(int i=1;i <= n;i ++)
		for(int j=1;j <= m;j ++)
			b[i][j] += b[i-1][j]+b[i][j-1]-b[i-1][j-1];
	for(int i=1;i <= n;i ++){
		for(int j=1;j <= m;j ++)
			printf("%d ",b[i][j]);
		cout << endl; 
	}
	return 0;
}

六、双指针算法

6.1 题目:最长连续不重复子序列

给定一个长度为n的整数序列,请找出最长的不包含重量数字的连续子序列,输出它的长度。

输入格式

第一行包含整数 n。
第二行包含 n 个整数(均在0-100000范围内),表示整数序列。

输出格式

其一行,包含一个整数。表示最长的不包含重复数字的连续子序列的长度。

数据范围:1 ≤ n ≤ 100000

输入样例:5        1 2 2 3 5

输出样例:3 

6.2 代码

#include <iostream>
using namespace std;

const int N = 100010;
int n,a[N];
int s[N]; //当前 j到 i这个区间里面,每一个数出现的次数 

int main(){
	cin >> n;
	for(int i=0;i < n;i ++)
		cin >> a[i];
	int res = 0;
	for(int i=0,j=0;i < n;i ++){
		s[a[i]] ++;
		//j < i在该题中可以省略,因为j >= i时区间一定没有重复的数 
		while(s[a[i]] > 1){
			s[a[j]] --;
			j ++;
		} 
		res = max(res,i-j+1);
	}
	cout << res << endl;
	return 0; 
}


七、位运算

#include <iostream>
using namespace std;

int main(){
	int n = 10; //二进制表示为1010 
	for(int k=3;k >= 0;k --)
		cout << (n>>k & 1); //右移k位再与1 
	return 0;
}

7.1 lowbit 运算 

lowbit ( n ) 定义为非负整数n在二进制表示下“最低位的1及其后边所有的构成的数值。

例如,n=10的十进制表示为(1010),则 lowbit ( n ) = 2。

7.2 题目:二进制中1的个数

给定一个长度为n的数列,请你求出数列中每个数的二进制表示中1的个数。

输入格式

第一行包含整数n。
第二行包含n个题数,表示整个数列。

输出格式

共一行,包含n个整教,其中的第 i 个数表示教列中的第 i 个数的二进制表示中1的个数。

数据范围:1  <= n <= 100000,0 <= 数列中元素的值 < 10^9

输入样例:5        1 2 3 4 5

输出样例:1 1 2 1 2

7.3 代码


#include <iostream>
using namespace std;

int lowbit(int x){
	return x & -x;
}
int main(){
	int n;
	cin >> n;
	while(n --){
		int x;
		cin >> x;
		int res = 0;
		while(x){
			x -= lowbit(x); //每次减去x的最后一位1
			res ++; 
		}
		cout << res << ' ';
	}
	return 0;
}


八、离散化

通俗地讲,“离散化”就是把无穷大集合中的若干个元素映射为有限集合以便于统计的方法。

vector<int> alls; //存储所有待离散化的值
sort(alls.begin,alls.end): //将所有值排序 
alls.erase(unique(alls.begin(), alls.end()), alls.end()); //去掉重复元素
//二分求出x对应的离散化的值
int find(int x){ //找到第一个大于等于x的位置 
	int l = 0,r = alls.size()-1;
	while (l < r){
		int mid = l + r >> 1;
		if (alls[mid] >= x)
			r = mid;
		else
			l = mid + 1; //映射到1,2,…,n 
	}
	return r + 1;
}

8.1 题目:区间和

假定有一个无限长的数轴。数轴上每个坐标上的数都是0。现在,我们首先进行n次操作,每次操作将某一位置x上的数加c。接下来,进行 m次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [ l,r ] 之间的所有数的和。

输入格式

第一行包含两个整数n和m。接下来n行,每行包含两个整数x和c。再接下里m行,每行包含两个整数和。

输出格式

共m行,每行输出一个询问中所求的区间内数字和。

数据范围

-10^9 <= x <= 10^9;1 <= n, m <= 10^5;-10^9 <= l <= r <= 10^9;-10000 <= c <= 10000。

输入样例:3 3        1 2        3 6        7 5        1 3        4 6        7 8

输出样例:8        0        5

8.2 代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

typedef pair<int,int> PII;
const int N = 300010; //n+2m 
int n,m;
int a[N],s[N]; //存入的数据,前缀和 
vector<int> alls; //存的所有要离散化的数 
vector<PII> add,query;

int find(int x){
	int l = 0,r = alls.size()-1;
	while (l < r){
		int mid = l + r >> 1;
		//求大于等于x的最小值下标 
		if (alls[mid] >= x)
			r = mid;
		else
			l = mid + 1; 
	}
	return r + 1; //映射到1,2,…,n 
}
 
int main(){
	cin >> n >> m;
	for(int i=0;i < n;i ++){
		int x,c;
		cin >> x >> c;
		add.push_back({x,c}); //需要用大括号 
		alls.push_back(x);
	}
	for(int i=0;i < m;i ++){
		int l,r;
		cin >> l >> r;
		query.push_back({l,r});
		//将左右边界加入到待离散化的数组里面 
		alls.push_back(l);
		alls.push_back(r);
	}
	sort(alls.begin(),alls.end());
	alls.erase(unique(alls.begin(),alls.end()),alls.end());

	//处理插入
	for(auto item : add){
		int x = find(item.first);
		a[x] += item.second; //在离散化之后的坐标位置上加上c 
	}
//	for(int i=0;i < add.size();i ++){
//		int x = find(add[i].first);
//		a[x] += add[i].second;
//	}

	//预处理前缀和
	for(int i=1;i <= alls.size();i ++)
		s[i] = s[i-1] + a[i]; 

	//处理询问
	for(auto item : query){
		int l = find(item.first);
		int r = find(item.second);
		cout << s[r]-s[l-1] << endl;
	} 
	return 0;
} 

pair 是将两个数据组合成一组数据,当需要这样的需求时就可以使用pair。另一个应用是,当一个函数需要返回两个数据时,可使用pair。

pair 的实现是一个结构体,主要的两个成员变量是first和second,可直接使用pair成员变量。

auto(自动)变量的原理是根据后面的值,来自己推测前面的类型是什么。

auto变量是C++语言程序中默认的存储类型,其作用就是为了简化变量的初始化。

unique函数功能是元素去重,即“删除”序列中所有相邻的重复元素(只保留一个)。

其把重复元素的位置被不重复的元素给占领,不停地把后面不重复的元素移到前面来。

返回值是一个迭代器,它指向去重后容器中不重复序列的最后一个元素的下一个元素。

unique函数通常和erase函数一起使用,来达到删除重复元素的目的。

使用unique函数时,容器的长度并没有发生变化,只是元素位置发生了变换。

erase(first,last)删:除容器中first到last之间的所有元素(左闭右开),不包括last所指的元素。

九、区间合并

9.1 题目:区间合并

给定n个区间 [ li,ri ],要求合并所有有交集的区间。注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。例如:[1,3] 和 [2,6] 可以合并为—个区间 [1,6]。

输入格式

第—行包含整数n.
接下来n行,每行包含两个整数 l 和 r。

输出格式

共—行,包含—个整数,表示合并区间完成后的区间个数。

数据范围

1 <= n <= 100000,-10^9  <= li <= ri <= 10^9。

输入样例:5        1 2        2 4        5 6        7 8        7 9

输出样例:3

9.2 代码

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

typedef pair<int,int> PII;
const int N = 100010;
vector<PII> segs;
int n;

void merge(vector<PII> &segs){
	vector<PII> res;
	sort(segs.begin(),segs.end()); //pair排序会优先于左端点 
	int st = -2e9,ed = -2e9; //初始化 
	for(auto seg : segs)
		if(ed < seg.first){ //增加区间 
			if(st != -2e9) //不能是最开始的初始区间 
				res.push_back({st,ed});
			st = seg.first,ed = seg.second;
		}
		else //更新区间 
			ed = max(ed,seg.second);
	if(st != -2e9) //防止输入的数组里面是没有任何区间的 
		res.push_back({st,ed}); //将最后的区间加入到答案里面  
	segs = res;	
}

int main(){
	cin >> n;
	for(int i=0;i < n;i ++){
		int l,r;
		cin >> l >> r;
		segs.push_back({l,r});
	}
	merge(segs);
	cout << segs.size() << endl;
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值