AcWing算法基础篇复习(C++)

本文探讨了快速排序、归并排序、前缀和等经典算法,并通过实例展示了它们在处理序列、矩阵、查找、逆序对计算等场景中的应用。此外,还涉及到了差分、子序列、区间和等问题的解决方案,揭示了这些算法在信息技术领域的广泛适用性。
摘要由CSDN通过智能技术生成

785. 快速排序

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤1000001≤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) >> 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, 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;
}

786. 第k个数

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

输入格式

第一行包含两个整数 nn 和 kk。

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

输出格式

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

数据范围

1≤n≤1000001≤n≤100000,
1≤k≤n1≤k≤n

输入样例:

5 3
2 4 1 5 3

输出样例:

3
//快选算法,O(n)
#include <iostream>
using namespace std;
const int N = 1e6 + 10;

int n, k;
int q[N];

int quick_sort(int l, int r, int k)
{
    if(l == r) return q[l];
    int x = q[l], i = l - 1, j = r + 1;    //该处定界时 x=q[l] 与python中不同的
    while(i < j)
    {
        while(q[++ i] < x);
        while(q[-- j] > x);
        if(i < j) swap(q[i], q[j]);
    }
    int sl = j - l + 1;
    if(k <= sl) 
        return quick_sort(l, j, k);       
    return quick_sort(j+1, r, k - sl);
    //c++快选中:x = q[l] 与(l, i-1, k)(i, r, k - sl)、(l, j, k)(j+1, r, k - sl) 配套
    //           x = q[r] 均不可配套
    //           x = q[(l+r+1)/2] 均不可配套
}
int main()
{
    scanf("%d%d", &n, &k);
    //cin>>a>>b
    for(int i = 0; i < n; i ++) scanf("%d", &q[i]);
    printf("%d", quick_sort(0, n - 1, k));
    
}

   //c++快排中:x = q[l] 仅与(q, l, j)(q, j+1, r) 配套   √
                          x = q[r] 仅与(q, l, i-1)(q, i, r) 配套
                          x = q[(l+r+1)/2] 均可配套

  //c++快选中:x = q[l] 与(l, i-1, k)(i, r, k - sl)、(l, j, k)(j+1, r, k - sl) 配套
                         x = q[r] 均不可配套
                         x = q[(l + r + 1) / 2] 均不可配套

  //python中解决边界问题:均用 x = q[(l+r+1) >> 1]


787. 归并排序

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

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤1000001≤n≤100000

输入样例:

5
3 1 2 4 5

输出样例:

1 2 3 4 5
//双指针问题 、分治问题
#include <iostream>
using namespace std;
const int N = 1e5 + 10;

int n;
int q[N], tmp[N];
void merge_sort(int q[], int l, int r)
{
    if(l >= r) return;
    int mid = l + r >> 1;    //此处 mid = l + r + 1 >> 1 错误
    int k = 0, i = l, j = mid + 1;
    
    merge_sort(q, l, mid);   //先将左右两部分排序,构造两个有序序列
    merge_sort(q, mid + 1, r);
  
    while(i <= mid && j <= r)
    {
        if(q[i] >= q[j])  tmp[k++] = q[j++];
        else tmp[k++] = q[i++];
    }
    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];  
    //对每两个序列的排序,l,r都是不一样的c
}
int main()
{
    cin >> 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. 逆序对的数量

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

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

输入格式

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

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

输出格式

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

数据范围

1≤n≤1000001≤n≤100000

输入样例:

6
2 3 4 5 6 1

输出样例:

5

 逆序对出现的情况有三种: 1. L--○------○---MID-------R
                                             2.L-------------MID--○--○---R
                                             3.L--------○----MID---○----R
 这三种情况 均可转换为第三种:
                                                   L-----i-----MID
                                          MID+1----j---------R
 当在归并排序时 遇到q[i] > q[j]的情况:
                                说明从i到MID都 > q[j] :则此时逆序对的数量 num += mid - i+ 1
 这样就能实现在 归并排序中完成逆序对数量的计算

#include <iostream>
using namespace std;
typedef long long LL;  //定义long long 数据类型 为LL
const int N = 1e5 + 10;

int n;
int q[N], tmp[N];

LL merge_sort(int q[], int l, int r)  //该处使用了long long 数据类型 LL,使用int型 数据过大会出现负数,超出表示范围
{
    if (l >= r) return 0;
    int mid = (l + r) >> 1;
    int i = l, j = mid + 1, k = 0; 
    LL num = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r);  //递归求解num
    while(i <= mid && j <= r)
    {
        if (q[i] > q[j])
        {
            tmp[k ++] = q[j ++];
            num += mid - i + 1;  //逆序对数量
        }else tmp[k ++] = q[i ++];
    }
    //扫尾
    while(i <= mid) tmp[k ++] = q[i ++];
    while(j <= r) tmp[k ++] = q[j ++];
    //将tmp[]中的值赋予q[]
    for(int i = l, j = 0; i <= r; i ++, j++) q[i] = tmp[j];
    return num;
}
int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++) scanf("%d", &q[i]);
    cout << merge_sort(q, 0, n - 1) << endl;
    return 0;
}


 789. 数的范围

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

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

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

输入格式

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

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

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

输出格式

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

如果数组中不存在该元素,则返回 -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

 二分模板:本质是找边界问题
 1.    int l = 0, r = n - 1;   //该处定义左右端点
        while(l < r)
        {
           int mid = (l + r ) >> 1;   //与下文r = mid对应
           if(二分check条件)  r = mid ;
           else l = mid + 1 ;
        }
 2.    int l = 0,r = n - 1;   //该处定义左右端点
        while(l < r)
        {
           int mid = (l + r + 1 ) >> 1; //与下文l = mid对应,避免死循环
           if(二分check条件)  l = mid ; 
           else r = mid - 1;
        }

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

int main()
{
    int n, m;
    int q[N];

    cin >> 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 << " ";
           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;
           }
           cout << r <<endl;   
       } 
    }
    return 0;
}



 790. 数的三次方根

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

输入格式

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

输出格式

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

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

数据范围

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

输入样例:

1000.00

输出样例:

10.000000
//该题中全部用double型数据,用float型错误
//double双精度浮点型数据
#include <iostream>
using namespace std;
double l = -10000, r = 10000;

int main()
{
    double x;   
    cin >> x;
    while(r - l > 1e-8)
    {
        double mid = (l + r)/2;
        if(mid*mid*mid >= x) r = mid; //mid的三次方在c++中不能用mid^3表示:该表示为异或
        else l = mid;
    }
    printf("%.6f",r); //输出小数点后六位
    return 0;
}

        

795. 前缀和

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

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

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

输入格式

第一行包含两个整数 nn 和 mm。

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

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

输出格式

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

数据范围

1≤l≤r≤n1≤l≤r≤n,
1≤n,m≤1000001≤n,m≤100000,
−1000≤数列中元素的值≤1000−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 main()
{
    int n, m, q[N], s[N];
    cin >> n >> m;
    int l, r;
    for(int i = 1; i <= n; i ++ ) 
    {
        scanf("%d", &q[i]);
        s[i] = s[i - 1] + q[i];
    }
    while(m--)
    {
        cin >> l >> r;
        cout << s[r] - s[l - 1] << endl;
   }
    return 0;
}

796. 子矩阵的和

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

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

输入格式

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

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

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

输出格式

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

数据范围

1≤n,m≤10001≤n,m≤1000,
1≤q≤2000001≤q≤200000,
1≤x1≤x2≤n1≤x1≤x2≤n,
1≤y1≤y2≤m1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000−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 = 1010;

int n, m, q;
int a[N][N],s[N][N];
int x1, y1, x2, y2;

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]);
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
        }
    }
    while(q--)
    {
        cin >> x1 >> y1 >> x2 >> y2;
        int num = s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
        cout << num << endl;
    }
    return 0;
}

 前缀和问题总结:

 序列前缀和问题:注意:所有的数组从下标1开始存储
 第一步:构造前缀和数组:s[i] = s[i - 1] + q[i]
 第二步:计算前缀和:[l,r]之间元素和:s[r] - s[l - 1] 

 矩阵前缀和问题:注意:所有的数组从下标1开始存储
 第一步:构造子矩阵前缀和数组:s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
 第二步:计算子矩阵前缀和:s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];


 797. 差分

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

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

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

输入格式

第一行包含两个整数 nn 和 mm。

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

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

输出格式

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

数据范围

1≤n,m≤1000001≤n,m≤100000,
1≤l≤r≤n1≤l≤r≤n,
−1000≤c≤1000−1000≤c≤1000,
−1000≤整数序列中元素的值≤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 n, m, l, r, c;
int a[N], b[N];

void insert(int l, int r, int c)
{
    b[l] += c;
    b[r + 1] -= c;
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) 
    {
       scanf("%d",&a[i]);
       insert(i, i, a[i]);  
    }
    while(m --)
    {
        cin >> l >> r >> c;
        insert(l, r, c);
    }
    for(int i = 1; i <= n; i ++) 
    {
        a[i] = a[i - 1] + b[i];
        cout << a[i] << " ";
    }
    return 0;
}

#差分问题:b[N] 为差分数组
#a1 = b1
#a2 = b1 + b2
#a3 = b1 + b2 + b3
#a4 = b1 + b2 + b3 + b4
#............
#1.构造差分数组: b[l] += c, b[r + 1] -= c
#2.计算操作后的数组: a[i] = a[i - 1] + b[i]

#前缀和问题:s[N]为前缀和矩阵
#s1 = a1
#s2 = a1 + a2
#s3 = a1 + a2 + a3 
#s4 = a1 + a2 + a3 + a4
#.......可求[l,r]之间的额元素和
#1.构造前缀和数组:s[i] = s[i - 1] + a[i]
#2.求[l,r]之间元素和:s[r] - s[l - 1]


 798. 差分矩阵

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

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

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

输入格式

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

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

接下来 qq 行,每行包含 55 个整数 x1,y1,x2,y2,cx1,y1,x2,y2,c,表示一个操作。

输出格式

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

数据范围

1≤n,m≤10001≤n,m≤1000,
1≤q≤1000001≤q≤100000,
1≤x1≤x2≤n1≤x1≤x2≤n,
1≤y1≤y2≤m1≤y1≤y2≤m,
−1000≤c≤1000−1000≤c≤1000,
−1000≤矩阵内元素的值≤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 = 1010;

int n, m, q; 
int x1, y1, x2, y2, c;
int [N][N],b[N][N];

//构造差分矩阵
void insert(int x1, int y1, int x2, int y2,int c)
{
    b[x1][y1] += c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y1] -= 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]);
            insert(i, j, i, j, a[i][j]);
        }
    }
    while(q --)
    {
        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 ++)
        {
            //子矩阵的和 a[]为b[]的前缀和矩阵,此处的a[]就是前缀和中的s[]
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j]; 
            printf("%d ",a[i][j]);
        }
        cout << endl;
    }
    return 0;
}

矩阵差分问题核心:构造差分数组公式: 
    b[x1][y1] += c
    b[x1][y2 + 1] -= c
    b[x2 + 1][y1] -= c
    b[x2 + 1][y2 + 1] += c)
问题解决思路:
1.循环输入原矩阵
2.利用insert函数构造插入原差分矩阵
3.加减操作,得到最终差分矩阵
4.利用前缀和公式计算操作后的a[]
差分问题是前缀和问题的延伸:差分数组b[]就是前缀和问题中的原始数组,差分问题中的a[]就是前缀和问题中的s[]
差分问题:a[]是b[]的前缀和(数组、矩阵)(以前缀和问题为基础)
前缀和问题:s[]是原数组的前缀和(序列前缀和,子矩阵的和)


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

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

输入格式

第一行包含整数 nn。

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

输出格式

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

数据范围

1≤n≤1051≤n≤105

输入样例:

5
1 2 2 3 5

输出样例:

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

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

int main()
{
    cin >> n;
    int res = 0, j = 0;
    for(int i = 0; i < n; i ++) 
    {
        cin >> a[i] ;
        s[a[i]] += 1;
        while(s[a[i]] > 1)  //check条件
        {
            s[a[j]] -= 1;
            j += 1;
        }
        res = max(res,i - j + 1);
    }
    cout << res;
    return 0;
}

该问题为双指针问题:

判断是否为双指针问题的关键为:i、j是否为单调移动

如本题中:------j--------------i--------------- :i、j一直向右移动满足单调条件

当前序列(一定满足不重复条件)的下一个状态是有新的a[i]加入,就只可以判断a[i]是否在之前满足条件的序列中重复出现的元素,若有则需要将j一直向右移动直到将之前的重复元素移去当前序列即可,右移过程中同时将s[a[j]] -= 1,还原状态。


 800. 数组元素的目标和

给定两个升序排序的有序数组 AA 和 BB,以及一个目标值 xx。

数组下标从 00 开始。

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

数据保证有唯一解。

输入格式

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

第二行包含 nn 个整数,表示数组 AA。

第三行包含 mm 个整数,表示数组 BB。

输出格式

共一行,包含两个整数 ii 和 jj。

数据范围

数组长度不超过 105105。
同一数组内元素各不相同。
1≤数组元素≤1091≤数组元素≤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 n, m, x;
int a[N], b[N];

int main()
{
    cin >> n >> m >> x;
    for(int i = 0; i < n; i ++) cin >> a[i];
    for(int j = 0; j < m; j ++) cin >> b[j];
    int j = m - 1;
    for(int i = 0;i < n; i ++)   //双指针算法模板
    {
        while(j >= 0 && a[i] + b[j] > x) j --;  
        if(a[i] + b[j] == x) printf("%d %d",i,j);
    }
}

本题中:----i  >>------------------------------ :i一直向右移动满足单调条件

               ------------------------<<  j------:j一直向左移动满足单调条件

双指针模板:

for(i = 0,j = 0;i<n;i ++)
{
    while(j<i && check(i,j)) j++
    //每道题目的具体逻辑
}

2816. 判断子序列

给定一个长度为 nn 的整数序列 a1,a2,…,ana1,a2,…,an 以及一个长度为 mm 的整数序列 b1,b2,…,bmb1,b2,…,bm。

请你判断 aa 序列是否为 bb 序列的子序列。

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

输入格式

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

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

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

输出格式

如果 aa 序列是 bb 序列的子序列,输出一行 Yes

否则,输出 No

数据范围

1≤n≤m≤1051≤n≤m≤105,
−109≤ai,bi≤109−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()
{
    cin >> n >> m;
    for(int i = 0; i < n; i ++) scanf("%d", &a[i]);
    for(int j = 0; j < m; j ++) scanf("%d", &b[j]);
    int i = 0, j = 0;
    while(i < n && j < m)
    {
        if(a[i] == b[j]) i ++;
        j ++;
    }
    if(i == n) printf("Yes");
    else printf("No");
}

本题中:----i  >>------------------------------ :i一直向右移动满足单调条件

              ------------------------  j >>----------:j一直向右移动满足单调条件


801. 二进制中1的个数

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

输入格式

第一行包含整数 nn。

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

输出格式

共一行,包含 nn 个整数,其中的第 ii 个数表示数列中的第 ii 个数的二进制表示中 11 的个数。

数据范围

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

输入样例:

5
1 2 3 4 5

输出样例:

1 1 2 1 2
#include <iostream>
using namespace std;

int lowbit(int x) //lowbit函数作用为找到x最后一位1
{
    return x & -x;  //&:位运算中:0&1=0 0&0=0 1&1=1
}

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

 本题中学习了lowbit函数:return x&-x 的作用,返回x的最后一位


 802. 区间和

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

现在,我们首先进行 nn 次操作,每次操作将某一位置 xx 上的数加 cc。

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

输入格式

第一行包含两个整数 nn 和 mm。

接下来 nn 行,每行包含两个整数 xx 和 cc。

再接下来 mm 行,每行包含两个整数 ll 和 rr。

输出格式

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

数据范围

−109≤x≤109−109≤x≤109,
1≤n,m≤1051≤n,m≤105,
−109≤l≤r≤109−109≤l≤r≤109,
−10000≤c≤10000−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;

typedef pair<int, int> PII;

const int N = 3e5 + 10;

int n, m, x, c, l, r;
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;
        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 ++)  //处理x, c
   {
       cin >> x >> c;
       alls.push_back(x);
       add.push_back({x,c});
   }
   for(int j = 0; j < m; j ++) //处理l, r
   {
       cin >> l >> r;
       alls.push_back(l);
       alls.push_back(r);
       query.push_back({l,r});
   }   
   //对alls去重,排序
   sort(alls.begin(), alls.end());
   alls.erase(unique(alls.begin(), alls.end()),alls.end());
   //unique函数找到重复元素的第一个,返回去重后的序列的最后一位+1  
   //处理插入
   for(auto item : add)
   {
       x = find(item.first);
       a[x] += item.second;
   }  
   //预处理前缀和
   for(int i = 1; i < alls.size() + 1; i ++)  s[i] = s[i - 1] + a[i];
   //处理询问
   for(auto item : query)
   {
       l = find(item.first) , r = find(item.second);
       cout << s[r] - s[l - 1] << endl;
   }
   return 0;
}


#为什么 alls加入了l,r并且一起排序:因为alls为下标数组,需要集齐所有出现的下标
#离散化目的:将元素集中在3*10^5内,将范围缩小很多,便于使用前缀和进行计算(a[l],a[r])之间的数据的和
#举例:(x,c):(200,1),(1001,2),(2000,3)  
#      (l,r):(1,3),(1000,1001),(2010,2200)
#解:定义:a[N]、s[N]、alls、query、add 数组
#    ①对于(x,c)的处理:将(x,c)加入alls和add:
#                      alls:(200,1001,2000)  add:({200,1},{1001,2},{2000,3})
#    ②对于(l,r)的处理:将(l,r)加入alls和query:
#                      alls:(200,1001,2000,1,3,1000,1001,2010,2200)  query:({1,3},{1000,1001},{2010,2200})
#    ③对alls下标数组排序、去重:alls:(1,3,200,1000,1001,2000,2010,2200)
#    ④find函数:找到x在alls中的位置:200 --> alls[2]  1001-->alls[4]  2000 -->alls[5]
#    ⑤为了便于使用前缀和求解:将求出的位置+1  并将要加的值加到a上:a[2+1] = a[3] = 1 a[4+1] = a[5] = 2  a[5+1]=a[6] = 3                          
#    此时已经使用alls这个下标数组完成了对离散数据的集中:转换过程:x --> alls[从零开始的某个下标r]=x -->  a[r+1]
#    ⑦find函数:找到l,r在alls中的位置(同上)
#    ⑧前缀和计算即可
# 重点:在整个变换过程中插入元素的相对位置并没有改变,只是元素之间的距离更紧凑了


 803. 区间合并

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

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

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

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

输入格式

第一行包含整数 nn。

接下来 nn 行,每行包含两个整数 ll 和 rr。

输出格式

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

数据范围

1≤n≤1000001≤n≤100000,
−109≤li≤ri≤109−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 = 1e5 + 10;

int n, l, r;
vector<PII> alls;

void merge(vector<PII> &alls)
{
    vector<PII> res;
    sort(alls.begin(), alls.end());
    int start = -2e9, end = -2e9;
    for(auto item : alls)
        if(end < item.first) 
        {
            if(start != -2e9) res.push_back({start, end});
            start = item.first, end = item.second;
        }
        else end = max(end, item.second);
        
    if (start != -2e9) res.push_back({start,end}); 
    alls = res;
}
int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)
    {
        cin >> l >> r;
        alls.push_back({l, r});
    }
    merge(alls);
    cout << alls.size() << endl;
}

 #st        ed    
 #|----------|  当前区间
 #①  |---|        next ∈ 当前区间   ed > next.st   --->  ed = max(ed, next.ed) 
 #②                |---------|    ed < next.st ---> 当前区间存入res
 #③          |---------|    ed = next.st   --->  ed = max(ed, next.ed)   更新ed
 #   or    |---------|      ed > next.st   --->  ed = max(ed, next.ed)


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: acwing算法基础课是一门针对算法学习的在线课程,在这门课程中,学生可以系统地学习和掌握算法基础知识,提高编程水平。为了方便学生学习,acwing提供了网盘服务。 acwing算法基础课网盘是一个用于存储课程资源的平台。通过这个网盘,学生可以下载课程讲义、代码模板以及补充材料等。这些资源都经过精心整理,供学生们参考和学习。 网盘中的资源是按照课程章节进行分类的,学生可以根据自己的学习需要,选择性地下载所需的资料。同时,网盘还提供了搜索功能,方便学生快速定位和获取所需资料。 acwing算法基础课网盘的使用对于学生们的学习非常有帮助。通过下载和学习这些资源,学生们可以更好地理解课程内容,加深对算法的理解。此外,学生们还可以通过研究代码模板,学习优秀的编程思想和技巧,提高自己的编程能力。 总之,acwing算法基础课网盘是一项非常便利和实用的服务,为学生们提供了更加全面和深入的学习资源,帮助他们更好地掌握和运用算法知识。 ### 回答2: acwing算法基础课是一门优质的算法学习资源,其中的课程内容丰富多样,涵盖了算法基础知识、数据结构、动态规划、图论等等。很多学习者都认为这门课程对他们的算法学习有很大的帮助。 网盘是指以网络为媒介,提供文件存储和下载服务的云存储平台。acwing算法基础课也提供了网盘服务,方便学习者下载课程资料并进行学习。 通过acwing算法基础课网盘,学习者可以方便地获取到课程的各种学习资料,包括讲义、习题集、代码示例等。这些资料可以帮助学习者更好地理解和掌握课程的内容。此外,网盘还提供了上传和分享功能,学习者可以将自己的学习心得、代码等资料分享给其他学习者,促进学习者之间的互相学习和交流。 acwing算法基础课网盘的优点不仅仅是方便快捷的下载和分享功能,还包括安全可靠的存储环境。学习者可以放心地将自己的学习资料上传到网盘进行备份,减少数据丢失的风险。同时,网盘还提供了多种存储空间容量的选择,满足学习者不同的需求。 总的来说,acwing算法基础课网盘为学习者提供了方便、安全和多样化的学习资源下载和分享服务,为学习者的算法学习和进步提供了有力的支持。如果你对算法感兴趣,我推荐你去尝试一下这门精彩的课程!
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值