通关基础算法 -- acwing

第一讲 基础算法

快排

原理

基于分治来做的

  1. 确定分界点 x :q[l],q[(l+r)/2],q[r],随机
  2. 把整个区间,根据 x ,把整个数据分成两部分。两部分不一定相等,使得左边部分都 <= x,右边部分都 >= x
  3. 递归得给左边排序,再递归得给右边排序

区间调整的实现方法

1.暴力解法

  1. a[],b[]
  2. 扫描一下 q[l] - q[r],if q[i] <= x --> x->a[]; else x->b[] ;
  3. a[] -> q[] , b[] -> q[]

2.优雅解法

  1. 用两个指针,一个指针 i 指向左边,一个指针 j 指向右边,两个指针分别往中间走(i 先动)
  2. if i 指向的数 小于 x,i 往后移一位 ;直到> x i 停下来,去移动 j
  3. if j 指向的数 大于 x,j 往前移一位;直到 < x j 停下来,交换 i 和 j 所指向的数,i 和 j 分别往中间移动一位
  4. 继续 2 和 3,直到 i 指针 和 j 指针相遇

举例

ex: 3 1 2 3 5

  1. 确定分界点:x = q[l] = 3
  2. i = 0,j = 4 q[i] = 3 不满足 小于 3,i 停下来 , q[j] = 5 > 3, j-- ; q[j] = 3 不满足 大于 3
  3. 交换 q[i(0)] 和 q[j(4)] ,数组变成:3 1 2 3 5,i 和 j 分别往中间移动 1 位 i = 1,j = 2
  4. q[i] = 1 < 3;i++; q[i] = 2 < 3;i++; q[i] = 3 不满足条件
  5. 此时 i 和 j 已经交叉了,结束
  6. 按照 j 来分 也就是 0 - 2 属于 左半部分 小于 3,3 - 4 属于 右半部分 大于 3
  7. i 指针 前面所有的数都是 小于等于 3的
  8. j 指针 后面所有的数都是 大于等于 3的

模板

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;

    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]);
    }
    quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

练习

785. 快速排序 - AcWing题库

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤100000

输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
#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+r>>1];
    int i = l - 1;
    int 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]);
        }
    }
    quick_sort(q,l,j);
    quick_sort(q,j+1,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;
}

786. 第k个数 - AcWing题库

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤100000,
1≤k≤n

输入样例:
5 3
2 4 1 5 3
输出样例:
3
#include<iostream>
using namespace std;

const int N = 1e6+10;
int n;
int k;
int q[N];

void quick_sort(int q[],int l,int r)
{
    if(l >= r)
    {
        return;
    }
    int x = q[l+r>>1];
    int i = l-1;
    int 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]);
        }
    }
    quick_sort(q,l,j);
    quick_sort(q,j+1,r);
}

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

归并排序

原理实现

基于分治来做

  1. 以整个数组的最中心来分为 left 和 right,确定分界点为 mid = (l+r)/2
  2. 先递归排序两边
  3. 归并—合二为一

归并的实现方法

​ 设置两个指针,分别指向两个数组的第一个位置,由于我们已经递归排序了两边数组,所以此时两个数组都是有序的,那么不难得出,我们两个指针指向的值都是这两个数组的最小值,此时我们比较两个指针指向的值,将较小的一个放到我们的res 数组中,然后指针+1,重复此操作,直到一个指针到终点,退出循环,再将剩余数据补充到 res 中

举例

left 数组:1 3 5 7 9

right 数组:2 4 5 8 10

此时两个指针分别指向 left[0] 和 right[0]

left[0] < right[0] --> res[0] = left[0] ,left指针++

left[1] > right[0] --> res[1] = right[0],right指针++

以此类推

最后 res 数组为 = {1,2,3,4,5,5,7,8,9,10}

模板

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, 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];
}

练习

787. 归并排序 - AcWing题库

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤100000

输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
#include<iostream>
using namespace std;

const int N = 100010;
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)/2;
    
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);
    int k = 0,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(int i = l,j = 0;i <= r;i++,j++)
    {
        q[i] = tmp[j];
    }
}

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;
}

788. 逆序对的数量 - AcWing题库

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

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

输入格式

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

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

输出格式

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

数据范围

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

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

1.左半边内部的逆序对数量: merge_sort(L,mid)

2.右半边内部的逆序对数量: merge_sort(mid+1,R)

3.[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zYWAE0ZE-1664524287527)(C:\Users\ShibuyaKanon\AppData\Roaming\Typora\typora-user-images\image-20220917214136290.png)]

这题最大逆序对数量是 5*10^9 会报int错,得用 long long

#include<iostream>
using namespace std;

const int N = 100010;
typedef long long LL;
int n;
int q[N];
int tmp[N];

LL merge_sort(int l,int r)
{
    if(l >= r)
    {
        return 0;
    }
    
    int mid = l+r>>1;
    
    LL res = merge_sort(l,mid) + merge_sort(mid+1,r);
    
    int i = l,j = mid + 1,k = 0;
    while(i <= mid && j <= r)
    {
        if(q[i] <= q[j])
        {
            tmp[k++] = q[i++];
        }else{
            tmp[k++] = q[j++];
            res += mid - i + 1;
        }
    }
    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];
    }
    return res;
}

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

整数二分

本质

如果有单调性的话 --> 我可以二分,反之不然

整个区间可以一分为二,我们定义了一个性质,右半边是满足这个性质的,但是左半边不满足

二分可以寻找这个性质的边界

  1. 如何二分左半部分的边界点:

  2. mid = l + r +1>> 1

  3. if(check(mid)) -> true:答案处于 [mid,r] -> 更新区间 l = mid else 答案处于 [l,mid-1] -> 更新区间 r = mid -1;

  4. 如何二分右半部分的边界点

  5. mid = 1+r >> 1

  6. if(check(mid)) -> true:答案处于 [l,mid] -> 更新区间 r = mid else 答案处于 [mid+1,r] -> 更新区间 l = mid+1;

模板

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

练习

789. 数的范围 - AcWing题库

给定一个按照升序排列的长度为 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

该题的 check:

  1. 寻找 x 的起始点 --> 找到一个区间使得区间里所有的数 >= x
  2. 如果 q[mid] >= x ,答案一定在 mid 的左边,且不包括 mid,所有更新区间为 r = mid 套用第一个模板
  3. 寻找 x 的结束点 --> 找到一个区间使得区间里的所有数 <= x
  4. 如果 q[mid] <= x ,答案一定在 mid 的右边,且mid 有可能是答案,所以更新区间为 l = mid 套用第二个模板,修改 mid = l+r+1>>1
#include<iostream>
using namespace std;

const int N = 100010;
int n;
int 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{
            printf("%d ",l);
            int l = 0,r= n-1;
            while(l < r)
            {
                int mid = l + r + 1 >> 1;
                if(q[mid] <= x)
                {
                    l = mid;
                }else{
                    r = mid - 1;
                }
            }
            printf("%d\n",l);
        }
    }
    return 0;
}

浮点数二分

原理

当我们的范围足够小的时候,我们就可以认为我们找到了答案

例如 r - l <= 10-6 ,我们认为他已经足够小,因为有整除,所以不需要处理边界问题

练习

790. 数的三次方根 - AcWing题库

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

输入格式

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

输出格式

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

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

数据范围

−10000≤n≤10000

输入样例:
1000.00
输出样例:
10.000000
#include<iostream>
using namespace std;

int main()
{
    double x;
    cin >> x;
    double l = -100,r = 100;
    while(r-l>1e-8)
    {
        double mid = (l+r)/2;
        if(mid*mid*mid>=x)
        {
            r = mid;
        }else{
            l = mid;
        }
    }
    
    printf("%.6lf",l);
    return 0;
}

高精度加法

大整数如何存储? --每一位存到数组里

例如:123456789 第 0 位存谁? – 存9

因为 如果 0位存最后一位,需要乘法的时候,在数组末尾添加数字要比数组开端添加数字方便

原理

模拟我们人加减法的过程

例如 123 + 89,我们先算3 + 9 = 11,进位,再算 3+8 = 11,再进位得 2,所以我们的答案是 212

那么假设我们有两个大整数分别存储在了 A[] , 和 B[] 中
$$
A_3 A_2A_1A_0\

  • B_2 B_1B_0\
    ------\
    C_3C_2C_1C_0
    $$
    C0 就是 (A0+B0)mod 10,如果 A0+B0 大于 10,那么下一次 就得进位(+1)

那么我们可以推导出 每一位Ci = Ai + Bi + t,t为上一次的进位

模板

// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);

    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
        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;
}
// C = A - B, 满足A >= B, A >= 0, B >= 0
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);
        if (t < 0) t = 1;
        else t = 0;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

练习

791. 高精度加法 - AcWing题库

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

输入格式

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

输出格式

共一行,包含所求的和。

数据范围

1≤整数长度≤100000

输入样例:
12
23
输出样例:
35
#include<iostream>
#include<vector>
#include<string>
using namespace std;


const int N = 1e6+10;

vector<int> add(vector<int>A,vector<int>B)
{
    vector<int> C;
    int t;
    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/=10;
    }
    if(t)
    {
        C.push_back(1);
    }
    return C;
}

int main()
{
    string a,b;
    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');
    }
    
    auto C = add(A,B);
    
    for(int i = C.size()-1;i >= 0;i--)
    {
        cout << C[i];
    }
}

高精度减法

原理

$$
A_3 A_2A_1A_0\

  • B_2 B_1B_0\
    ------\
    C_3C_2C_1C_0
    $$

那我们可归纳为
C i = A i − B i − t = { A i − B i − t , C i > = 0 A i − B i + 10 − t , C i < 0 C_i = A_i-B_i-t= \begin{cases}A_i-B_i-t,Ci>=0\\A_i-B_i+10-t,C_i<0\end{cases} Ci=AiBit={AiBit,Ci>=0AiBi+10t,Ci<0

A − B = { A − B , A > = B − ( B − A ) , A < = B A - B = \begin{cases} A-B,A>=B\\-(B-A),A<=B\end{cases} AB={AB,A>=B(BA),A<=B

模板

// C = A - B, 满足A >= B, A >= 0, B >= 0
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);
        if (t < 0) t = 1;
        else t = 0;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

练习

792. 高精度减法 - AcWing题库

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

输入格式

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

输出格式

共一行,包含所求的差。

数据范围

1≤整数长度≤10^5

输入样例:
32
11
输出样例:
21
#include<iostream>
#include<vector>
#include<string>
using namespace std;

bool cmp(vector<int> A,vector<int> B)
{
    if(A.size()!=B.size())
    {
        return A.size()>B.size();
    }else{
        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;
    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);
        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;
    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');
    }
    
    if(cmp(A,B))
    {
         auto C = sub(A,B);
    
        for(int i = C.size()-1;i >= 0;i--)
        {
            cout << C[i];
        }
    }else{
        auto C = sub(B,A);
        cout << "-";
        for(int i = C.size()-1;i >= 0;i--)
        {
            cout << C[i];
        }
    }
   
}

高精度乘法

$$
A_3 A_2A_1A_0\

  • a\\
    

------\
C_3C_2C_1C_0
$$

例子

A = 123 B=12

C0 = (3x12)%10 = 6 t1 = (3x12)/10 = 3

C1 = (2x12+t)%10 = 7 t2 = 2

答案是:1476

模板

// C = A * b, A >= 0, b >= 0
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;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}

练习

793. 高精度乘法 - AcWing题库

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

输入格式

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

输出格式

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

数据范围

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

输入样例:
2
3
输出样例:
6
#include<iostream>
#include<vector>
#include<string>
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;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}

int main()
{
    int b;
    string a;
    vector<int> A;
    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];
    }
}

高精度除法

例如:11√1234

首先看第一位,1除以11,显然不够,上0,余数是1,我们把余数乘10+下一位的2,得到12

,得到1,余数为1,以此类推得到答案是112

模板

// A / b = C ... r, A >= 0, b > 0
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 %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

练习

794. 高精度除法 - AcWing题库

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

输入格式

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

输出格式

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

数据范围

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

输入样例:
7
2
输出样例:
3
1
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
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);
        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;
    
    cin >> a >> b;
    
    vector<int> A;
    for(int i = a.size()-1;i >= 0;i--)
    {
        A.push_back(a[i]-'0');
    }
    
    int r;
    auto C = div(A,b,r);
    
    for(int i = C.size()-1;i >= 0;i--)
    {
        cout << C[i];
    }
    cout << endl;
    cout << r;
}

前缀和

原理

我们假定有一个长度为 n 的数组
a 1 a 2 a 3 . . . a n a_1 a_2a_3...a_n a1a2a3...an
定义前缀和数组为:
S i = a 1 + a 2 + . . . + a n , s 0 = 0 S_i=a_1+a_2+...+a_n,s_0 = 0 Si=a1+a2+...+an,s0=0
1.如何求 si

for(int i = 1;i <= n;i++)
{
	s[i] = s[i-1]+a[i];
}

2.作用

能快速地求出来,数组中一段数的和

例如 求 [l,r] 的和,用前缀和数组来求即为 Sr - Sl-1
S r = a 1 + a 2 + a 3 + . . . + a l − 1 + a l + . . . + a r S l − 1 = a 1 + a 2 + a 3 + . . . + a l − 1 那么 S r − S l − 1 = a l + a l + 1 + . . . + a r S_r = a_1+a_2+a_3+...+a_{l-1}+a_l+...+a_r\\ S_{l-1}=a_1+a_2+a_3+...+a_{l-1}\\ 那么 S_r-S_{l-1} = a_l+a_{l+1}+...+a_r Sr=a1+a2+a3+...+al1+al+...+arSl1=a1+a2+a3+...+al1那么SrSl1=al+al+1+...+ar

模板

S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]

练习

795. 前缀和 - AcWing题库

输入一个长度为 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 = 100010;
int a[N],s[N];
int n,m;


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++)
    {
        s[i] = s[i-1] + a[i];
    }
    
    for(int i = 0;i < m;i++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",s[r]-s[l-1]);
    }
    
    return 0;
}

二维前缀和

原理

假定原数组为 a i , j , 前缀和数组为 S i , j S i , j 表示以( i , j )这个点左上角所有元素的和我们可先减去左半边矩阵的和 S x 2 , y 2 − S x 2 , y 1 − 1 再减去上半部分的矩阵的和 S x 2 , y 2 − S x 2 , y 1 − 1 − S x 1 − 1 , y 2 但是我们发现,左上角的小正方形被减了两次,加回来 S x 2 , y 2 − S x 2 , y 1 − 1 − S x 1 − 1 , y 2 + S x 1 − 1 , y 1 − 1 假定原数组为 a_{i,j},前缀和数组为S_{i,j}\\ S_{i,j}表示以(i,j)这个点左上角所有元素的和 我们可先减去左半边矩阵的和\\ S_{x_2,y_2} - S_{x_2,y_1-1}\\ 再减去上半部分的矩阵的和\\ S_{x_2,y_2} - S_{x_2,y_1-1}-S_{x_1-1,y_2}\\ 但是我们发现,左上角的小正方形被减了两次,加回来\\ S_{x_2,y_2} - S_{x_2,y_1-1}-S_{x_1-1,y_2}+S_{x_1-1,y_1-1} 假定原数组为ai,j,前缀和数组为Si,jSi,j表示以(ij)这个点左上角所有元素的和我们可先减去左半边矩阵的和Sx2,y2Sx2,y11再减去上半部分的矩阵的和Sx2,y2Sx2,y11Sx11,y2但是我们发现,左上角的小正方形被减了两次,加回来Sx2,y2Sx2,y11Sx11,y2+Sx11,y11

如何推导 S[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];
	}
}

模板

//S[i, j] = 第i行j列格子左上部分所有元素的和
//以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

练习

796. 子矩阵的和 - AcWing题库

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

#include<iostream>
const int N = 1010;

int n,m,q;
int a[N][N],s[N][N];

using namespace std;

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,x2,y1,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][y1-1]);
    }
    
    return 0;
}

差分

原理

差分是前缀和的逆运算

给定一个 长度为 n 的a 数组,构造一个 b 数组,使得:
a i = b 1 + b 2 + b 3 + . . . + b i b 1 = a 1 b 2 = a 2 − a 1 b n = a n − a n − 1 a_i=b_1+b_2+b_3+...+b_i\\ b_1=a_1\\ b_2=a_2-a_1\\ b_n=a_n-a_{n-1} ai=b1+b2+b3+...+bib1=a1b2=a2a1bn=anan1

b就称为 a 的差分,a就称为b的前缀和

模板

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

练习

797. 差分 - AcWing题库

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

接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r][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 = 100010;
int n,m;
int a[N],b[N];

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;
}

二维差分

原理

b [ x 1 ] [ y 1 ] + = c b [ x 2 + 1 ] [ y 1 ] − = c b [ x 1 ] [ y 2 + 1 ] − = c b [ x 2 + 1 ] [ y 2 + 1 ] + = c b [ i ] [ j ] 记录的就是和相邻元素的差 b[x_1][y_1]+=c\\ b[x_2+1][y_1]-=c\\ b[x1][y2+1]-=c\\ b[x_2+1][y_2+1]+=c\\ b[i][j] 记录的就是和相邻元素的差 b[x1][y1]+=cb[x2+1][y1]=cb[x1][y2+1]=cb[x2+1][y2+1]+=cb[i][j]记录的就是和相邻元素的差

模板

给以(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

练习

798. 差分矩阵 - AcWing题库

输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1)和 (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。

每个操作都要将选中的子矩阵中的每个元素的值加上 cc。

请你将进行完所有操作后的矩阵输出。

输入格式

第一行包含整数 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≤矩阵内元素的值≤100−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 = 1010;
int n,m,q;
int a[N][N],b[N][N];

void insert(int x1,int x2,int y1,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++)
        {
            insert(i,i,j,j,a[i][j]);
        }
    }
    
    while(q--)
    {
        int x1,y1,x2,y2,c;
        
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
        
        insert(x1,x2,y1,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]);
        }
        printf("\n");
    }
    
    return 0;
}

双指针算法

原理

  1. 指向的是两个序列
  2. 指向一个序列,例如快排

双指针的核心作用 就是 可以优化

所有双指针算法都是 O(n) 的

优化的本质是 找到 i 和 j 的规律

for(int i = 0;i <= n;i++)
{
	for(int j = 0;j <= n;j++)
	{
	
	}
}

//O(n^2) 的复杂度
//使用 双指针算法 可以将其优化到 O(n)

模板

for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;

    // 具体问题的逻辑
}
常见问题分类:
    (1) 对于一个序列,用两个指针维护一段区间
    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作

例子

比如我们给定一个形同 abc def ghi 的字符串,要求把每个单词输出

那么我们可以定义两个指针,第一个指针指向单词开头,当第二个指针没有走到字符串末尾并且所指向的字符不为空格的时候,他自增。

在输出结束后,让 i = j,来跳过整个已经输出的区间。

#include<iostream>
#include<string.h>

using namespace std;

int main()
{
	char str[1000];
	gets(str);
	int n = strlen(str);
	for(int i = 0;i < n;i ++)
	{
		int j = i;
		while(j < n && str[j]!=' ')
		{
			j++;
		}
		//具体逻辑
		for(int k = i;k < j;k++)
		{
			cout << str[k];
		}
        cout << endl;
        
        i = j;
	}
}

练习

799. 最长连续不重复子序列 - AcWing题库

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

输入格式

第一行包含整数 n。

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

输出格式

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

数据范围

1≤n≤10^5

输入样例:
5
1 2 2 3 5
输出样例:
3
//朴素做法
for(int i = 0;i < n;i++)
{
	for(int j = 0;j < n;j++)
	{
		if(check(j,i))
		{
			res = max(res,i-j+1);
		}
	}
}

//双指针做法
#include<iostream>
using namespace std;

const int N = 1e6+10;
int q[N];
int s[N];
int n;
int res = 0;
    

int main()
{
    scanf("%d",&n);
    
    for(int i = 0;i < n;i++)
    {
        scanf("%d",&q[i]);
    }
    
    res = 0;
    for(int i = 0,j=0;i < n;i ++)
    {
        s[q[i]]++;
        while(s[q[i]]>1)
        {
            s[q[j]]--;
            j++;
        }
        res = max(res,i-j+1);
    }
    
    cout << res << endl;
    return 0;
}

800. 数组元素的目标和 - AcWing题库

给定两个升序排序的有序数组 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];
int b[N];
int n,m,x;

int main()
{
    scanf("%d%d%d",&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 (j >= 0 && a[i] + b[j] == x)
        {
            cout << i << ' ' << j << endl;
        }
    }

    
    return 0;
}

2816. 判断子序列 - AcWing题库

给定一个长度为 nn 的整数序列 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
#include<iostream>
using namespace std;

const int N = 1e5+10;
int n,m;
int a[N],b[N];


int main()
{
    scanf("%d%d",&n,&m);
    
    for(int i = 0;i < n;i++)
    {
        scanf("%d",&a[i]);
    }
    
    for(int i = 0;i < m;i++)
    {
        scanf("%d",&b[i]);
    }
    
    int j = 0;
    for(int i = 0;i < n;i++)
    {
        for(;j <= m;j++)
        {
            if(a[i] == b[j])
            {
                //cout << i << " " << j << endl;
                j++;
                break;
            }
        }
        //cout << j << " ";
        if(j >= m+1 && i < n)
        {
            cout << "No";
            return 0;
        }
    }
    cout << "Yes";
    return 0;
}

位运算

求 整数 n 二进制表示里,第 k 位数字是几?

n = 15 = ( 1111 ) 2 n = 15 = (1111)_2 n=15=11112

  1. 先把第 k 位移到最后一位 n >> k
  2. 看个位是几 x&1
  3. 总结:n >> k&1

例子

例如输出 10 的二进制表达

#include<iostream>

int main()
{
	int n = 10;
	for(int k = 3;k >= 0;k--)
	{
		cout << (n >> k)&1;
	}
    return 0;
}

lowbit(x) 作用:返回 x 的最后一位 1

例如 x = 1010,lowbit(x) = 10

x = 101000,lowbit(x) = 1000

x & (-x) = x & (~x+1)

x = 1010…100…0

~x=0101…011…1

~x+1=0101…100…0

x & (~x+1) = 0…010…0

可以利用 lowbit 返回 x 二进制 1 的个数

原理:每一次把最后一位1减掉,当x = 0的时候,里面就没有1了,减了多少次就是有多少个1

模板

求n的第k位数字: n >> k & 1
返回n的最后一位1:lowbit(n) = n & -n

练习

801. 二进制中1的个数 - AcWing题库

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

输入格式

第一行包含整数 n。

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

输出格式

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

数据范围

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

输入样例:
5
1 2 3 4 5
输出样例:
1 1 2 1 2
#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);
            res++;
        } 
        cout << res << endl;
    }
    
}

离散化

原理

假如我们有一对数值 范围是 0 - 109

但是个数比较少,只有比如 105的数字,我们需要以这些值位下标来做

所以我们需要这个序列映射到 从 0 开始的连续的自然数

比如我们假定有数组:a[] :1,3,100,200,500000

我们将 1 映射到 0,3 映射到 1,100 映射到 2,200 映射到 3,500000 映射到 4,这个过程就是离散化

  1. 且注意 a 中可能会有重复元素,所以我们需要去重
  2. 如何算出 x 离散化后的值? – 二分

模板

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
}

练习

802. 区间和 - AcWing题库

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

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

接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [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 = 300010;
int n,m;
int a[N],s[N];

typedef pair<int,int> PLL;

vector<int> alls;
vector<PLL> add,query;

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;
        }else{
            l = mid + 1;
        }
    }
    return r + 1;
}

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;
    }
    
    //  预处理前缀和
    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;
}

区间合并

给我们很多很多区间,这两个区间有交集,我们合并成一个区间

例如 [1,9] 和 [3,13] 可以合并为 [1,13]

原理

  1. 按所有区间的左端点排序
  2. 扫描整个区间,把所有可能有交点的区间进行合并
    1. 每次维护一个区间 [ st , ed ]
    2. 假设扫描到了第 i 个区间 [ sti , edi ],有三种可能的关系
      1. ed > sti > st && edi < ed
      2. ed >= sti > st && edi > ed
      3. sti > ed

对于 i : st 和 ed 不变

对于 ii:ed = edi,st不变

对于 iii 不变

例子

我们假定有区间如下:

[ 1 , 2 ] , [ 2 , 4 ] , [ 5 , 6 ] , [ 7 , 8 ] , [ 7 , 9 ]

第一次合并区间后得到

[ 1 , 4 ] , [ 5 , 6 ] , [ 7 , 8 ] , [ 7 , 9 ]

因为 [ 5 , 6 ] 大于 ed(4) 所以第一个区间和以后所有的区间不会有任何交集,把 [ 1 , 4 ] 放入答案

同理 [ 5 , 6 ] 和以后所有的区间也不会有任何交集,放入答案中

故而最后答案为 [ 1 , 4 ] , [ 5 , 6 ] , [ 7 , 9 ]

模板

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

练习

803. 区间合并 - AcWing题库

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

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

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

例如:[1,3][1,3] 和 [2,6][2,6] 可以合并为一个区间 [1,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;
const int N = 1000010;
int n;
vector<PII> segs;

void merge(vector<PII> &segs)
{
    vector<PII> 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(seg.second,ed);
        }
    }
    
    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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
sqli-labs是一个SQL注入学习平台,通过完成一系列的关卡来学习和实践SQL注入漏洞的利用方法。根据引用的描述,首先需要在浏览器中打开"http://127.0.0.1/sqli-labs/",访问平台的首页。然后点击"Setup/reset Database"按钮以创建数据库,创建表并填充数据。 完成了上述设置后,可以开始挑战关卡。 sqli-labs通关1关:根据引用提供的内容,可以在URL中加入"?sort=1 and (updatexml(1,concat(0x5c,(select group_concat(password,username) from users),0x5c),1))"来进行注入。这样就能够获取到users表中的密码和用户名的组合。 sqli-labs通关2关:根据引用提供的内容,可以在账户密码后面加入"1',updatexml (1,concat(0x5c,(select group_concat(username,password) from users),0x5c),1))#"来进行注入。这样就能够获取到users表中的用户名和密码的组合。 sqli-labs通关3关:通关3关的方法没有在提供的引用中找到相关信息。 sqli-labs通关4关:根据引用提供的内容,可以在URL中加入"?sort=1 and (updatexml(1,concat(0x5c,(select group_concat(password,username) from users),0x5c),1))"来进行注入。这样就能够获取到users表中的密码和用户名的组合。 sqli-labs通关5关:通关5关的方法没有在提供的引用中找到相关信息。 请注意,为了安全起见,在进行实际操作时,请确保仅在合法和授权的环境中进行,并遵守法律和道德规范。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [详细sqli-labs(1-65)通关讲解](https://blog.csdn.net/dreamthe/article/details/123795302)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Sqli-labs通关全解---关于Sqli-lab--1](https://blog.csdn.net/cyynid/article/details/128629421)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值