经典基础算法

目录

基础算法

快速排序

1.快速排序

2.第k个数

归并排序

3.归并排序

4. 逆序对的数量

二分

5.数的范围

6.数的三次方根

高精度

7.高精度加法

8.高精度减法

9.高精度乘法

10.高精度除法

前缀和与差分

11.前缀和

12.子矩阵的和

13.差分

14.差分矩阵

双指针算法

15.最长连续不重复子序列

16.数组元素的目标和

17.判断子序列

位运算

18.二进制中1的个数

离散化

19.区间和

区间合并

20.区间合并


基础算法

快速排序

1.快速排序

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤100000

输入样例:

5
3 1 2 4 5
//快速排序,从大到小划分,先排两边,递归地向下细分,每次分完 j在i前或者两个相等

#include<iostream>
using namespace std;
const int N=1e5+10;
int q[N];
int n;
//快速排序模板,熟练掌握
void quick_sort(int q[],int l,int r){
    //递归终止条件
    if(l==r) return;
    
    //划分加交换
    int i=l-1,j=r+1,x=q[(l+r)/2];
    while(i<j){
        do i++;while(x>q[i]);
        do j--;while(x<q[j]);
        if(i<j) swap(q[i],q[j]);
    }
    
    //细分
    quick_sort(q,l,j);
    quick_sort(q,j+1,r);
}


int main(){
    cin>>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;
}

2.第k个数

给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。

输入格式

第一行包含两个整数 n 和 k。

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

输出格式

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

数据范围

1≤n≤100000,
1≤k≤n

输入样例:

5 3
2 4 1 5 3

输出样例:

3
//自己做法,直接弄个快速排序求第k个数
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N];
int n,k;
void quick_sort(int q[],int l,int r){
    if(l==r) return;
    int i=l-1,j=r+1,x=q[(l+r)/2];
    while(i<j){
        do i++;while(q[i]<x);
        do j--;while(q[j]>x);
        if(i<j) swap(q[i],q[j]);
    }
    
    quick_sort(q,l,j);
    quick_sort(q,j+1,r);  
}
int main(){
    cin>>n>>k;
    for(int i=0;i<n;i++) cin>>a[i];
    quick_sort(a,0,n-1);
    cout<<a[k-1]<<endl;
}
//快排基础上改进,每次都分到第k个数的那边
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N];
int n,k;
int quick_sort(int q[],int l,int r,int k){
    if(l==r) return q[l];
    int i=l-1,j=r+1,x=q[(l+r)/2];
    while(i<j){
        do i++;while(q[i]<x);
        do j--;while(q[j]>x);
        if(i<j) swap(q[i],q[j]);
    }
    if(j-l+1>=k) quick_sort(q,l,j,k);
    //要注意将第k个数写进函数需替换成当前的第k-(j-l+1)个数
    else quick_sort(q,j+1,r,k-(j-l+1));  
}
int main(){
    cin>>n>>k;
    for(int i=0;i<n;i++) cin>>a[i];
    cout<<quick_sort(a,0,n-1,k)<<endl;
}

归并排序

3.归并排序

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤100000

输入样例:

5
3 1 2 4 5

输出样例:

1 2 3 4 5
//归并排序,从小到大合并,先划分,再合并。

#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],tmp[N];
int n;
void merge_sort(int a[],int l,int r){
    if(l==r) return;
    //划分,以mid为界限
    int mid=(l+r)/2;
    merge_sort(a,l,mid),merge_sort(a,mid+1,r);

    //归并,
    int k=0,i=l,j=mid+1;
    while(i<=mid&&j<=r){
        if(a[i]<a[j]) tmp[k++]=a[i++];
        else tmp[k++]=a[j++];
    }
    //归并中的扫尾工作
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=r) tmp[k++]=a[j++];
    
    //赋值回原数组
    for(i=l,k=0;i<=r;i++,k++)
        a[i]=tmp[k];
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    merge_sort(a,0,n-1);
    for(int i=0;i<n;i++) cout<<a[i]<<" ";
    return 0;
}

4. 逆序对的数量

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

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

输入格式

第一行包含整数 n,表示数列的长度。

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

输出格式

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

数据范围

1≤n≤100000,
数列中的元素的取值范围[1,109]。

输入样例:

6
2 3 4 5 6 1

输出样例:

5
//求逆序对
//在分治后的每一层合并中顺便求出逆序对数量是这个题想法的由来,
//归并排序分治我们求的是从小到大的顺序,我们所求的逆序对恰好是逆序数量,与归并排序不谋而合。
//例如[3,4,1,2]中q[0]>q[2],则q[0],q[1]都与q[2]成逆序对,而q[mid]与q[i]有mid-i+1个数字,因此逆序对增加mid-i+1
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],tmp[N];
int n;
typedef long long ll;
ll merge_sort(int a[],int l,int r){
    
    //递归结束条件    
    if(l==r) return 0;
    
    //划分
    int mid=(l+r)/2;
    ll res=merge_sort(a,l,mid)+merge_sort(a,mid+1,r);
    
    //归并
    int k=0,i=l,j=mid+1;
    while(i<=mid&&j<=r){
        if(a[i]<=a[j]) tmp[k++]=a[i++];
        else{
            tmp[k++]=a[j++];
            res+=mid-i+1;
        }
    }
    
    
    //收尾
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=r) tmp[k++]=a[j++];
    
    //赋值回原数组
    for(int i=l,j=0;i<=r;i++,j++)  a[i]=tmp[j];
    
    return res;
}

int main(){
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    cout<<merge_sort(a,0,n-1)<<endl;
    return 0;
}

二分

5.数的范围

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

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

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

输入格式

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

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

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

输出格式

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

如果数组中不存在该元素,则返回 -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
//整数二分
#include<iostream>

using namespace std;
const int N = 1e5 + 10;
int a[N];
int n, q;

int main() {
    cin >> n >> q;
    for (int i = 0; i < n; i++) cin >> a[i];
    while (q--) {
        int x;
        scanf("%d", &x);
        int l = 0, r = n - 1;
        int mid;
        
        //分左边界
        while (l < r) {
            mid = (l + r) / 2;
            if (a[mid] >= x) r = mid;
            else l = mid + 1;
        }
      
        if (a[l] != x) cout << -1 << " " << -1 << endl;
        
        else {
            cout<<l<<" ";
            l = 0, r = n - 1;
            //分右边界
            while (l < r) {
                //因为l=mid,如果不加1,两个值差1的时候会等于l,会陷入死循环
                mid = (l + r + 1) / 2;
                if (a[mid] <= x) l = mid;
                else r = mid - 1;
            }

            cout << r << endl;
        }

    }
    return 0;
}

6.数的三次方根

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

输入格式

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

输出格式

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

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

数据范围

−10000≤n≤10000

输入样例:

1000.00

输出样例:

10.000000
//浮点数二分
//-22~22之间用二分法即可
#include<iostream>
using namespace std;
int main(){
    double x;
    double l=-22,r=22;
    double mid;
    cin>>x;
    //浮点数二分中,区间不能设置r>l,因为r永远也大不了l,只能无限接近。
    while(r-l>1e-10){
        mid=(l+r)/2;
        if(mid*mid*mid>=x) r=mid;
        else l=mid;
    }
   printf("%lf\n",l);
    return 0;
}

高精度

7.高精度加法

给定两个正整数(不含前导 0),计算它们的和。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的和。

数据范围

1≤整数长度≤100000

输入样例:

12
23

输出样例:

35
//只有加法无前导码
#include<iostream>
#include<vector>
using namespace std;
string a,b;
vector<int> A,B;
vector<int> add(vector<int>&A,vector<int>&B){
    int t=0;
    vector<int> C;
    //核心进位加法
    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=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');
    for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
    auto C=add(A,B);
    //输出
    for(int i=C.size()-1;i>=0;i--) cout<<C[i];
    cout<<endl;
    return 0;
}

8.高精度减法

给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的差。

数据范围

1≤整数长度≤105

输入样例:

32
11

输出样例:

21
//减法有前导码
#include<iostream>
#include<vector>
using namespace std;
vector<int> A, B;
string 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;
}
//做减法
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 = 1;
        else t = 0;
    }
    //删除前导码
    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');
    for (int i = b.size()-1; i >= 0; i--) B.push_back(b[i] - '0');

    if (cmp(A, B)) {
        auto C = sub(A, B);
        for (int i = C.size() - 1; i >= 0; i--) cout << C[i];
        cout << endl;
    } else {
        auto C = sub(B, A);
        cout << "-";
        for (int i = C.size() - 1; i >= 0; i--) cout << C[i];
        cout << endl;

    }


    return 0;
}

9.高精度乘法

给定两个非负整数(不含前导 0) A 和 B,请你计算 A×B 的值。

输入格式

共两行,第一行包含整数 A,第二行包含整数 B。

输出格式

共一行,包含 A×B 的值。

数据范围

1≤A的长度≤100000,
0≤B≤10000

输入样例:

2
3

输出样例:

6
#include<iostream>
#include<vector>

using namespace std;
string a;
int b;
vector<int> A;
//高精度乘法
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() {
    cin >> a >> b;

    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');

    auto C = mul(A, b);
    for (int i = C.size() - 1; i >= 0; i--) cout << C[i];
    cout << endl;
    return 0;
}

10.高精度除法

给定两个非负整数(不含前导 0) A,B,请你计算 A/BA/B 的商和余数。

输入格式

共两行,第一行包含整数 A,第二行包含整数 B。

输出格式

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

数据范围

1≤A的长度≤100000,
1≤B≤10000,
B 一定不为 0

输入样例:

7
2

输出样例:

3
1
#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

vector<int> A;

vector<int> div(vector<int> A, int b, int &r) {
    r = 0;
    vector<int> C;
    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() {
    int r, b;
    string a;
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0');
    auto C = div(A, b, r);
    for (int i = C.size() - 1; i >= 0; i--) cout << C[i];
    cout << endl << r;
}

前缀和与差分

11.前缀和

输入一个长度为 n 的整数序列。

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

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

输入格式

第一行包含两个整数 n 和 m。

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

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

输出格式

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

数据范围

1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000

输入样例:

5 3
2 1 3 6 4
1 2
1 3
2 4

输出样例:

3
6
10
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],s[N];
int n,m;
int main(){
    cin>>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;
        cin>>l>>r;
        //求一维差值
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}

12.子矩阵的和

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

对于每个询问输出子矩阵中所有数的和。

输入格式

第一行包含三个整数 n,m,q。

接下来 n 行,每行包含 m 个整数,表示整数矩阵。

接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。

输出格式

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

数据范围

1≤n,m≤1000,
1≤q≤200000,
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 = 1e3 + 10;
int a[N][N], s[N][N];
int n, m, q;

int main() {
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> 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;
        cin >> x1 >> y1 >> x2 >> y2;
        //求二维差值
        cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;
    }
    return 0;
}

13.差分

输入一个长度为 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>

using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];

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

int main() {
    int n, m;
    cin >> n >> m;
    //难点,差分数组初始为0,插入类似于区间加值而已
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        insert(i, i, x);
    }
    while (m--) {
        int l, r, x;
        cin >> l >> r >> x;
        insert(l, r, x);
    }
    for (int i = 1; i <= n; i++)
        a[i] = a[i - 1] + b[i];
    for (int i = 1; i <= n; i++)
        cout << a[i] << " ";
    cout << endl;
    return 0;
}

14.差分矩阵

输入一个 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 = 1e3 + 10;
int a[N][N], b[N][N];

void insert(int x1, int y1, int x2, int y2, int x) {
    //二维数组的添加和打补丁 4步法
    b[x1][y1] += x;
    b[x1][y2 + 1] -= x;
    b[x2 + 1][y1] -= x;
    b[x2 + 1][y2 + 1] += x;
}

int main() {
    int n, m, q;
    cin >> n >> m >> q;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            int x;
            cin >> x;
            insert(i, j, i, j, x);
        }
    while (q--) {
        int x1, y1, x2, y2, x;
        cin >> x1 >> y1 >> x2 >> y2 >> x;
        insert(x1, y1, x2, y2, x);
    }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            //求前缀和的公式
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++)
                cout << a[i][j]<<" ";
            cout << endl;
        }
        return 0;
}

双指针算法

15.最长连续不重复子序列

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

输入格式

第一行包含整数 n。

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

输出格式

共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。

数据范围

1≤n≤105

输入样例:

5
1 2 2 3 5

输出样例:

3
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;

int a[N],s[N];
int n,res;
int main(){
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0,j=0;i<n;i++){
        s[a[i]]++;
        //不需要判断j<=i,同时满足单调性,因为若大区间无重复的数所以小区间也无重复的数
        while(s[a[i]]>1){
            s[a[j]]--;
            j++;
        }
        res=max(res,i-j+1);
    }
    cout<<res;
    return 0;
}

16.数组元素的目标和

给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。

数组下标从 0 开始。

请你求出满足 A[i]+B[j]=x 的数对 (i,j)。

数据保证有唯一解。

输入格式

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

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

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

输出格式

共一行,包含两个整数 i 和 j。

数据范围

数组长度不超过 105。
同一数组内元素各不相同。
1≤数组元素≤109

输入样例:

4 5 6
1 2 4 7
3 4 6 8 9

输出样例:

1 1
//有序数组,一个往右遍历,一个往左遍历即可,绝对能保证唯一性
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int main(){
    int n,m,s;
    cin>>n>>m>>s;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<m;i++) cin>>b[i];
    for(int i=0,j=m-1;i<n;i++){
        //每一个i找一个最靠左的j
        while(j>=0&&b[j]+a[i]>s) j--;
        if(a[i]+b[j]==s) {
            cout<<i<<" "<<j<<endl;
            return 0;
        }
    }
    return 0;
}

17.判断子序列

给定一个长度为 n 的整数序列 a1,a2,…,an 以及一个长度为 m 的整数序列 b1,b2,…,bm。

请你判断 a 序列是否为 b 序列的子序列。

子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5} 是序列 {a1,a2,a3,a4,a5} 的一个子序列。

输入格式

第一行包含两个整数 n,m。

第二行包含 n 个整数,表示 a1,a2,…,an。

第三行包含 m 个整数,表示 b1,b2,…,bm。

输出格式

如果 a 序列是 b 序列的子序列,输出一行 Yes

否则,输出 No

数据范围

1≤n≤m≤105,
−109≤ai,bi≤109

输入样例:

3 5
1 3 5
1 2 3 4 5

输出样例:

Yes
//思路 两个都从0开始,匹配成功了a就往后走一步,匹配不成功b就往后走一步,到最后只要a走完了就输出yes,否则就输出no
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int n,m;
int main(){
    cin>>n>>m;
    
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<m;i++) cin>>b[i];
    int i=0,j=0;
    while(i<n&&j<m){
        if(a[i]==b[j]) i++;
        j++;
    }
    if(i==n) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}

位运算

18.二进制中1的个数

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

输入格式

第一行包含整数 n。

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

输出格式

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

数据范围

1≤n≤100000,
0≤数列中元素的值≤109

输入样例:

5
1 2 3 4 5

输出样例:

1 1 2 1 2
最常用的两种操作
整数n的二进制表示中第k位是几
n=15=1111 个位是第0位,十位是第一位
n>>k&1 

lowbit(x):返回x的最后一位1
x&-x  返回二进制从最后一个1开始的十进制的值
x=1010-->10
x=101000-->1000
#include<iostream>
using namespace std;
const int N=1e5+;
int a[N],b[N];
int lowbit(int x){
    return x&-x;
}
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
         cin>>a[i];
        while(a[i]){
            a[i]-=lowbit(a[i]);//求最后一位1
            b[i]++;
        }
    }
    for(int i=0;i<n;i++) cout<<b[i]<<" ";
    cout<<endl;
    return 0;
}

离散化

19.区间和

假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。

现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。

接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。

输入格式

第一行包含两个整数 n 和 m。

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

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

输出格式

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

数据范围

−109≤x≤109,
1≤n,m≤105,
−109≤l≤r≤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 = 3e5 + 10;
int a[N], s[N];
typedef pair<int, int> PII;
vector<int> alls;
vector<PII> add, query;

int find(int x) {
    int l = 0, r = alls.size() - 1;
    int mid;
    //返回的是第一个大于等于x的下标
    while (l < r) {
        mid = (l + r) / 2;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 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());
    //将x中的下标加上数
    for (int i = 0; i < n; 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 (int i = 0; i < m; i++) {
        int l = find(query[i].first);
        int r = find(query[i].second);
        cout << s[r] - s[l - 1] << endl;
    }

    return 0;
}

区间合并

20.区间合并

给定 n 个区间 [li,ri],要求合并所有有交集的区间。

注意如果在端点处相交,也算有交集。

输出合并完成后的区间个数。

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

输入格式

第一行包含整数 n。

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

输出格式

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

数据范围

1≤n≤100000,
−109≤li≤ri≤109

输入样例:

5
1 2
2 4
5 6
7 8
7 9

输出样例:

3
//区间合并,要点先排序后比较将大于的把前一个放进去,将小于等于的扩展成最大区间
//将最后一个也放进去

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;
typedef pair<int, int> PII;
vector<PII> segs;

void merge(vector<PII> &segs) {
    vector<PII> c;
    int l = -2e9, r = -2e9;
    sort(segs.begin(), segs.end());
    for (auto item:segs) {
        if (r < item.first) {
            if (r != -2e9) c.push_back({l, r});
            l = item.first;
            r = item.second;
        } else
            r = max(item.second, r);
    }
    if (l != -2e9) c.push_back({l, r});
    segs = c;
}

int main() {
    int n;
    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;
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迟意..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值