快速排序

快速排序

题目:

给定你一个长度为n的整数数列。

请你使用快速排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 n。

第二行包含 n 个整数(所有整数均在1~109109范围内),表示整个数列。

输出格式

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

数据范围

1≤n≤1000001≤n≤100000

  1. 输入样例:
  2. 5
  3. 3 1 2 4 5
  4. 输出样例:
  5. 1 2 3 4 5

程序

using namespace std;
const int N=100000;
int p[N];
void quick_sort(int p[],int l,int r){
    if(l>=r) return ;
    int i = l-1,j=r+1,x=p[l+r>>1];
    while(i<j){
        do i++;while(p[i]<x);
        do j--;while(p[j]>x);
        if(i<j) swap(p[i],p[j]);

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

题目2

题目2
给定一个长度为n的整数数列,以及一个整数k,请用快速选择算法求出数列的第k小的数是多少。

输入格式
第一行包含两个整数 n 和 k。

第二行包含 n 个整数(所有整数均在$1-~10^9$范围内),表示整数数列。

输出格式
输出一个整数,表示数列的第k小数。

数据范围
$1≤n≤1000000,
1≤k≤n$

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

程序

#include <iostream>
using namespace std;
const int N=100000;
int a[N];
int quick_sort_k(int a[],int l,int r,int k){
    if(l>=r) return q[l] ;
    int i=l-1;j=r+1,x=q[l+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]);
    }

    sl=j-l+1;
    if(k<=sl) return quick_sort_k(q,l,j,k);
    else return  quick_sort_k(q,j+1,r,k-sl);//k-sl 因为整个区间第K小的数是右半边区间的k-sl小的数
}

int main(){
    int n,k;
    cin >> n >> k ;
    for(int i=0;i<n;i++)sccanf("%d",a[i]);
    cout << quick_sort_k(q,0,n-1,k)<< endl;;
    return 0;
}

时间复杂度

 n *(1+1/2+1/4 +.....) < 2n      O(n)

归并排序

模板

题目

题目:给定你一个长度为n的整数数列。

请你使用归并排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 n。

第二行包含 n 个整数(所有整数均在1~109109范围内),表示整个数列。

输出格式

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

数据范围

$1≤n≤1000001≤n≤100000$

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

程序

#include<iostream>
using namespace std;
const int N =100000;
int q[N],temp[N];
long long res=0;
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 i=l,j=mid+1,k=0;
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) temp[k++]=q[i++];
        else { 
            temp[k++]=q[j++];
        }
    }
    while(i<=mid)temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];

    for(i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
}
int main(){
    int n;
    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]);
}

题目2

逆序对的数量
题目描述
给定一个长度为$n$的整数数列,请你计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 $i $个和第$ j $个元素,如果满足$ i < j 且 a[i] > a[j]$,则其为一个逆序对;否则不是。

输入格式
第一行包含整数$n$,表示数列的长度。

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

输出格式
输出一个整数,表示逆序对的个数。

数据范围
$1≤n≤100000$

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

程序

#include<iostream>
using namespace std;
const int N =100010;
int q[N],temp[N];
long long res=0;
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 i=l,j=mid+1,k=0;
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) temp[k++]=q[i++];
        else { 
            res += mid-i+1;
            temp[k++]=q[j++];
        }
    }
    while(i<=mid)temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];

    for(i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&q[i]);
    }
    merge_sort(q,0,n-1);
    cout << res<<endl;
    for(int i=0;i<n;i++)printf("%d ",q[i]);
}

二分查找

整数的二分

数的范围

给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。

对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。

如果数组中不存在该元素,则返回“-1 -1”。

输入格式

第一行包含整数n和q,表示数组长度和询问个数。

第二行包含$n$个整数(均在1~10000范围内),表示完整数组。

接下来$q$行,每行包含一个整数k,表示一个询问元素。

输出格式

共$q$行,每行包含两个整数,表示所求元素的起始位置和终止位置。

如果数组中不存在该元素,则返回“-1 -1”。

数据范围

$1≤n≤1000001≤n≤100000
1≤q≤100001≤q≤10000
1≤k≤100001≤k≤10000$

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

程序

using namespace std;
const int N=100000;
int a[N];
int q,k,n;
int main(){
    cin >> n >> q;
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    while(q--){
        scanf("%d",&k);
        //二分查找
        int l=0,r=n-1;
        while(l<r){
           int mid =l+r>>1;
           if(a[mid]>=k) r= mid;
           else l=mid+1;
        }//eof l=r
        if(a[l]!=k) cout << "-1 -1"<<endl;
        else{
            cout << l << " ";
            int l=0,r=n-1;
            while(l<r){
                int mid = l+r+1 >> 1;
                if(a[mid]<=k) l=mid;
                else r=mid-1;
            }
            cout << l << endl;
        }
    }

    return 0;
}

实数的二分

数的三次立方根

给定一个浮点数n,求它的三次方根。

输入格式

共一行,包含一个浮点数n。

输出格式

共一行,包含一个浮点数,表示问题的解。

注意,结果保留6位小数。

数据范围

$−10000≤n≤10000−10000≤n≤10000$

输入样例:
1000.00
输出样例:
10.000000

程序

#include<math.h>
using namespace std;

int main(){
    double n=0;
    cin >> n;
    double l=-10000,r=10000;
    while(r-l>1e-8){
        double mid =(l+r)/2;
        if(pow(mid,3)<=n) l=mid;
        else r=mid;
    }
    printf("%.6f",l);

}

高精度加法

A+B A的长度可能为 10^6

模板

题目

题目描述
定两个正整数,计算它们的和。

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

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

数据范围
$1≤整数长度≤100000$

样例
输入样例:
12
23
输出样例:
35

程序

#include <iostream>
#include <vector>
using namespace std;
vector<int> A,B;
string a,b;

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++){
        if(i<A.size()) t+=A[i];
        if(i<B.size()) t+=B[i];
        C.push_back(t%10);// 用t 代表 A[i]+B[i]+t   C[i] = (A[i]+B[i]+t)%10
        t/=10; //判断是否有进位
    }
    if(t) C.push_back(t);
    return C;
}
int main() {
    cin >> a >> b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');  // A = [1,2,3,4,5]
    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;
}

高精度减法

题目描述
给定两个正整数,计算它们的差。

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

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

数据范围
1≤整数长度≤105

样例
输入样例:
32
11
输出样例:
21

//A>=B return ture
bool cmp(vector<int> &A,vector<int> &B){
    if(A.size()!=B.size()) return A.size()>B.size(); //如果位数不等,长度长的大
    else{
        for(int i=0;i<A.size();i++){
            if(A[i]!=B[i]) return A[i]>B[i]; //如果位数相等,从前向后依次比较
        }
        return true;// 位数相等,数值也相等
    }
}

vector<int> sub(vector<int>&A,vector<int>&B){
    vector<int> C;
    int t =0;
    for(int i=0;i<A.size();i++){
        t = A[i]-t;
        if(i<B.size()) t-=B[i];
        C.push_back((t+10)%10);
        if(t>=0) t=0;
        else t=1;
    }
    while(C.size()>1&&C.back()==0) C.pop_back();
    return C;
}
int main() {
    cin >> a >> b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');  // A = [1,2,3,4,5]
    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);
        cout << '-';
        for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
    }
    return 0;
}

高精度乘法

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

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

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

数据范围
1≤A的长度≤100000,
1≤B≤10000

样例
输入样例:
2
3
输出样例:
6

vector<int> mul(vector<int> &A,int b){
    vector<int> C;
    int t=0;
    for(int i=0;i<A.size();i++){
        t+=A[i]*b;
        C.push_back(t%10);
        t/=10;
    }
    if(t) C.push_back(t);
    while(C.size()>1&&C.back()==0) C.pop_back();
    return C;
}
//乘法
int main(){
    string a;
    int b;
    vector<int> A;
    cin >> a >> b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0'); // A
    auto C =  mul(A,b);
    for(int i =C.size()-1;i>=0;i--)printf("%d",C[i]);
    return 0;
}

高精度除法

题目描述
给定两个正整数A,B,请你计算 A / B的商和余数。

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

输出格式
共两行,第一行输出所求的商,第二行输出所求余数。

数据范围
1≤A的长度≤100000,
1≤B≤10000

样例
输入样例:
7
2
输出样例:
3
1

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);
        r = r%b;
    }
    reverse(C.begin(),C.end());
    while(C.size()>1&&C.back()==0) C.pop_back();
    return C;
}
//除法
int main(){
    string a;
    int b,r=0;  //r 余数
    vector<int> A;
    cin >> a >> b;
    for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0'); // A
    auto C =  div(A,b,r);
    for(int i =C.size()-1;i>=0;i--)printf("%d",C[i]);
    cout << endl << r << endl;
    return 0;
}

前缀和

一维数组的前缀和

题目描述
输入一个长度为n的整数序列。

接下来再输入m个询问,每个询问输入一对l, r。

对于每个询问,输出原序列中从第l个数到第r个数的和。

输入格式
第一行包含两个整数n和m。

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

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

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

#include <iostream>
using namespace std;
const int N=100010;
int a[N],S[N];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i =1;i<=n;i++)scanf("%d",&a[i]); //i要从1开始!!!
    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]); // i 从1 开始的原因
    }
    return 0;
}

二维数组的前缀和

题目描述
输入一个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

输入样例

3 4 3 
1 7 2 4
3 6 2 8 
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

输出

17
27
21
#include <iostream >
using  namespace std;

const int N=10000;
int a[N][N],s[N][N];
int main(){
    int n,m,q;
    cin >> n >> m >> q;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
        }
    }
    //构造s
    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,x2,y1,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d\n",s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);
    }
    return 0;
}

差分

一维差分

说明

模板

给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

题目

题目描述
输入一个长度为n的整数序列。

接下来输入$m$个操作,每个操作包含三个整数$l, r, c,$表示将序列中$[l, r]$之间的每个数加上$c$。

请你输出进行完所有操作后的序列。

输入格式
$第一行包含两个整数n和m。$

$第二行包含n个整数,表示整数序列。$

$接下来m行,每行包含三个整数l,r,c,表示一个操作。$

输出格式
$共一行,包含n个整数,表示最终序列。$

数据范围
$1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000$

样例

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

程序
#include <iostream>
const int N=100000+10;
int a[N],b[N];//a 原数组 b 差分数组
using namespace std;
void insert(int l,int r,int x){
    b[l]+=x;
    b[r+1]-=x;
}
int main(){
    int n,m;
    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]);//相当于在[i,i]插入 a[i]

    while(m--){
        int l,r,c;
        scanf("%d%d%d",&l,&r,&c);
        insert(l,r,c);
    }
    // 由b生成a  b的前缀和
    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;
}

二维差分

说明

模板

给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c

题目

题目描述
输入一个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$

样例

输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2

程序

#include <iostream>
using namespace std;
const int N = 1002;
int a[N][N],b[N][N];
int n,m,q;
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(){
    cin >> 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++){
            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);
    }
    //b的前缀和
    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]);
            }
            puts("");
        }

}

位运算

说明

模板

求n的第k位数字: n >> k & 1
返回n的最后一位1:lowbit(n) = n & -n   //作用: 10100 --> 100   1010110 --> 10

题目

二进制中1的个数

程序
//x 二进制中1的个数
#include <bitset>
#include <iostream>
using namespace std;
//返回x的最后一位1  例如 10100 --> 100   1010110 --> 10
 int lowbit(const int x){
     return x&(-x);
 }
 int main(){
    int x,i=0;
    cin >> x;
    cout << bitset<sizeof(int)*8>(x) <<endl; // 调用库函数打印二进制数
    for(int k=31;k>=0;k--) cout << (x>>k&1); // 利用位运算-x的二进制表中第k位是几? x>>k  &1  的方法打印二进制
    cout << endl;
    while(x){
        x-=lowbit(x); //  10100 - 100 --> 10000 -10000 --> 00000 不断减去为1 的后几位
        i++;
    }
    cout  << "number of bit 1 : " << i << endl;
    return 0;
}

双指针算法

说明

模板

for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;
    // 具体问题的逻辑
}
常见问题分类:
    (1) 对于一个序列,用两个指针维护一段区间
    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作

题目

1、最长序列

题目描述

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

输入格式
第一行包含整数$n$。

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

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

数据范围
$1≤n≤100000$

样例

输入样例:
5
1 2 2 3 5
输出样例:
3

程序
using namespace std;
const int N =100010;
int a[N],s[N];
int main(){
    int n;
    int res=0;
    cin >> n;
    for(int i=0;i<n;i++){
        cin >> a[i];
    }
    for(int i=0,j=0;i<n;i++){
        s[a[i]]++;
        while(s[a[i]]>1) {
            s[a[j]]--;
            j++;
        }
        res = max(res,i-j+1);

    }
    cout << res <<endl;
    return 0;
}

2、数组元素的目标和

题目描述

$给定两个升序排序的有序数组$A$和$B$,以及一个目标值$x$,请你求出满足$A[i] + B[j] = x$的数对$(i, j)。

$数据保证有唯一解。$

输入格式
$第一行包含三个整数n,m,x,分别表示A的长度,B的长度以及目标值x。$

$第二行包含n个整数,表示数组A。$

$第三行包含m个整数,表示数组B.$

输出格式
$共一行,包含两个整数 i 和 j。$

数据范围
$数组长度不超过1000000。$
$同一数组内元素各不相同。$
$1≤数组元素≤10^9$

样例

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

程序
#include <iostream>
using namespace std;
const int N=1000010;
int A[N];
int B[N];
int main(){
    int n,m,x;
    cin>> n>>m >> x;
    for(int i=0;i<n;i++){
        scanf("%d",&A[i]);
    }
    for(int i=0;i<m;i++) scanf("%d",&B[i]);
    for(int i=0,j=m-1;i<n;i++){
        while(j>0&&A[i]+B[j]>x) j--;
        if(A[i]+B[j]==x) printf("%d %d",i,j);
    }
    return  0;
}

离散化

说明

实际上其主要思想是用一个数组来(这里记为all)存储大小两个数组之间的映射关系:

小的数组的下标和all的下标是一一对应的;
大的数组的下标是all存的值。

但是只有all数组还是不够的,all只是把大小数组的映射关系保存下来了,我们还需要一个find()函数,这样每次我们给它喂一个大数组的下标,它就会吐出来一个小数组的下标,这样就可以通过大数组的下标来访问小数组了。

步骤分为两步 去重 、二分

模板

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;
    }
    return r + 1; // 映射到1, 2, ...n
}

题目描述

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

输入格式
$第一行包含两个整数$n$和$m。

$接下来$ n$ 行,每行包含两个整数$x$和$c。

$再接下里 $m$ 行,每行包含两个整数$l$和$r。

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

数据范围
$−109,
1≤n,m≤10^5,
−109,
−10000≤c≤10000$

样例

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

程序

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 300100;// l r x
vector<int> alls;
vector<pair<int,int>> add, query;
int a[N];
int s[N];

int find(int x){
    int l =0,r = alls.size()-1;
    while(l<r){
        int mid = l+r >>1;
        if(alls[mid] >= x) r=mid; //找第一个大于x的数
        else l =mid +1;
    }
    return r+1; // 因为要用前缀和,所以从1开始

}
int main(){
    int n,m;
    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){
        a[find(item.first)]+= item.second;
    }

    //前缀和构建s[] s[x] = s[x-1]+a[x]
    for(int i=1;i<= alls.size();i++) s[i] = s[i-1]+a[i];
    //前缀和 计算[l,r]数
    for(auto item : query){
        int l=find(item.first),r=find(item.second);
        cout << s[r] - s[l-1]<< endl;
    }
    return 0;
}

区间合并

说明

Acwing 笔记 - 图1

模板

// 将所有存在交集的区间合并
void merge(vector<PII> &segs)
{
    vector<pair<int,int>> res;
    sort(segs.begin(), segs.end());
    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;
}

题目描述

给定n个区间[l, r],合并所有有交集的区间,输出合并完成后的区间个数。

例如:[1,3]和[2,6]可以合并为一个区间[1,6]。

输入格式
第一行包含整数$n$。

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

输出格式
共一行,包含一个整数,表示合并区间完成后的区间个数。

样例

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

程序

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<pair<int,int>> regs;

vector<pair<int,int>> merge(vector<pair<int ,int >> &segs){
    vector<pair<int,int>> res;
    sort(segs.begin(),segs.end());
    int start = -2e9,end = -2e9; //无穷远
    for(auto seg:segs){
        if(end < seg.first){
            if(start!=-2e9)  res.push_back({start,end});
            start = seg.first;end = seg.second;
        } else{
            end = max(end,seg.second);
        }
    }
   if(start!=-2e9) res.push_back({start,end}); //记得加上最后一个,判断条件是为了避免空数组
   return res;
}


int main(){
    int n;
    cin >> n;
    for(int i=0;i<n;i++){
       int l,r;
       cin >> l>>r;
       regs.push_back(pair<int,int>(l,r));
       //regs.push_back({l,r});  // 另一种形式
    }
    vector<pair<int,int>> res = merge(regs);
    cout << res.size() <<endl;
    return 0;
}