算法学习记录

1-1. 快速排序代码实现:

#include <iostream>
#include <cstdio>

using namespace std;

const int N = 1e6 + 10;

int q[N];
int n;

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;
    
    int x = q[l + r >> 1]; // Attention: select the middle number as partition
    int i = l - 1, j = r + 1;
    while (i < j)
    {
        do i++; while (q[i] < x);
        do j--; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    } // 循环结束后,i和j的取值只有两种情况:i = j 或 i = j + 1
    
    quick_sort(q, l, j); // Attention
    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;
}

1-2. 快速选择算法找出第 k 个数

#include <iostream>
#include <cstdio>

using namespace std;

const int N = 1e5 + 10;
int q[N];

int quick_select(int q[], int l, int r, int k)
{
    if (l >= r) return q[l];
    
    int x = q[l + r >> 1];
    int i = l - 1, j = r + 1;
    while (i < j)
    {
        do i++; while (q[i] < x);
        do j--; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    
    if (k <= j - l + 1) return quick_select(q, l, j, k);
    else return quick_select(q, j + 1, r, k - (j - l + 1));
}

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

2-1. 归并排序代码实现:

#include <iostream>

using namespace std;

const int N = 1e6 + 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;
    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]) 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;
}

2-2. 逆序对的数量

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int q[N], tmp[N];

long long findReversedPair(int q[], int l, int r) {
    if (l >= r) return 0;
    
    int mid = l + r >> 1;
    long long res = findReversedPair(q, l, mid) + findReversedPair(q, 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 (int i = l, k = 0; i <= r; i++, k++) q[i] = tmp[k];
    
    return res;
}

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", &q[i]);
    
    long long res = findReversedPair(q, 0, n - 1);
    
    cout << res << endl;
    
    return 0;
}
  1. 整数二分查找
#include <iostream>

using namespace std;

const int N = 1e6 + 10;

int q[N];
int n, m;

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; // look for the left boundary
            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; // Attention: l + r + 1
                if (q[mid] <= x) l = mid; // look for the right boundary
                else r = mid - 1;
            }
            cout << l << endl;
        }
    }
    
    return 0;
}
  1. 浮点数二分查找
#include <iostream>

using namespace std;

int main()
{
    double x;
    cin >> x;
    double px = abs(x); // the positive x
    
    double l = 0, r = (px > 1) ? px : 1;
    while (r - l > 1e-8)
    {
        double mid = (l + r) / 2; // Attention: float number cannot use the binary operator '>>'
        if (mid * mid * mid > px) r = mid;
        else l = mid;
    }
    
    if (x < 0) l = -l;
    
    printf("%lf", l);
    
    return 0;
}

【注】本题的数据类型只能用 double,不能用 float,因为 float 的精度(即有效数位)只有7位,循环体要求小数点后 8 位,若用 float,有可能会超时。

  1. 高精度运算

用一个数组来存放高精度数,如:

原始数字:123456789
存储:
Rank: 0 1 2 3 4 5 6 7 8
a[i]: 9 8 7 6 5 4 3 2 1

5-1. 高精度加法代码如下:

#include <iostream>
#include <vector>

using namespace std;

vector<int> add(vector<int> &A, vector<int> &B) // Attention: A and B are usually large, so use reference to them to avoid copying
{
    vector<int> C; // the result vector
    
    int t = 0; // carry, initialised to 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]; // now t equals to "A[i] + B[i] + t"
        C.push_back(t % 10);
        t /= 10;
    }
    
    if (t) C.push_back(1);
    
    return C;
}

int main()
{
    string a, b; // since the input are large, we use "string" to read them in
    vector<int> A, B;
    
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0'); // Attention: '1' - '0' = 1
    for (int j = b.size() - 1; j >= 0; j--) B.push_back(b[j] - '0');
    
    auto C = add(A, B);
    
    for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);
    
    return 0;
}

5-2. 高精度减法代码:

#include <iostream>
#include <vector>

using namespace std;

bool operator>=(const vector<int> &A, const 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(const vector<int> &A, const 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;
}

int main()
{
    string a, b;
    vector<int> A, B;
    
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i--) A.push_back(a[i] - '0'); // Attention!
    for (int i = b.size() - 1; i >= 0; i--) B.push_back(b[i] - '0'); // Attention!
    
    if (A >= B) // 注意:">="运算符已重载
    {
        auto C = sub(A, B);
        for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);
    }
    else
    {
        auto C = sub(B, A);
        cout << '-'; // or " printf("-") ";
        for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);
    }
    
    return 0;
}

5-3. 高精度乘法:

#include <iostream>
#include <vector>

using namespace std;

vector<int> mul(const vector<int> &A, int b)
{
    vector<int> C;
    
    for (int i = 0, t = 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()
{
    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');
    
    auto C = mul(A, b);
    
    for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);
    
    return 0;
}

5-4. 高精度除法

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

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--) printf("%d", C[i]);
    cout << endl << r << endl;
    
    return 0;
}

6.前缀和

给定原数组 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an,则前缀和数组 { S n } \{S_n\} {Sn} 定义为:
S i = a 1 + a 2 + ⋯ + a i (注意:下标从1开始) S_i = a_1 + a_2 + \cdots + a_i \quad\text{(注意:下标从1开始)} Si=a1+a2++ai(注意:下标从1开始)
特别地,规定 S 0 = 0 S_0 = 0 S0=0

前缀和数组的递推式: S i = S i − 1 + a i S_i = S_{i-1} + a_i Si=Si1+ai

前缀和数组的作用:在 O ( 1 ) O(1) O(1) 时间内,对区间 [ l , r ] [l, r] [l,r] 内的数组 a l , ⋯   , a r a_l, \cdots, a_r al,,ar 求和,即:
∑ i = l r a i = S r − S l − 1 \sum \limits _{i=l}^{r} a_i = S_r - S_{l - 1} i=lrai=SrSl1

加速 cin 的读取:

ios::sync_with_stdio(false); // 副作用:关掉这个开关之后,在程序中就不能再混用scanf/printf和cin/cout了

数据输入规模大于等于一百万时,建议用 scanf,否则建议用 cin。(scanf 可能会比 cin 快一倍)

6-1. 一维前缀和:

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

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

6-2.二维前缀和(矩阵)

二维前缀和的递推式:
S i , j = S i , j − 1 + S i − 1 , j − S i − 1 , j − 1 + a i , j S_{i, j} = S_{i, j-1} + S_{i-1, j} -S_{i-1, j-1} + a_{i, j} Si,j=Si,j1+Si1,jSi1,j1+ai,j
二维前缀和的作用:在 O ( 1 ) O(1) O(1) 时间内,对区域 ( x 1 , y 1 ) (x_1, y_1) (x1,y1)~ ( x 2 , y 2 ) (x_2, y_2) (x2,y2) 内的元素求和:
∑ i = x 1 j = y 1 i = x 2 j = y 2 a i , j = S x 2 , y 2 − S x 2 , y 1 − 1 − S x 1 − 1 , y 2 + S x 1 − 1 , y 1 − 1 \sum \limits _{\substack{i=x_1\\j=y_1}}^{\substack{i=x_2\\j=y_2}} a_{i,j} = S_{x_2, y_2} - S_{x_2, y_1-1} - S_{x_1 - 1, y_2} + S_{x_1 - 1, y_1 -1} i=x1j=y1i=x2j=y2ai,j=Sx2,y2Sx2,y11Sx11,y2+Sx11,y11
二维前缀和代码:

#include <iostream>

using namespace std;

const int N = 1010;

int a[N][N], S[N][N]; // 注意这里是二维矩阵

int main()
{
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%d", &a[i][j]);
    
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            S[i][j] = S[i - 1][j] + S[i][j - 1] - S[i - 1][j - 1] + a[i][j];
    
    while (q--)
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", S[x2][y2] - S[x2][y1 - 1] - S[x1 - 1][y2] + S[x1 - 1][y1 - 1]);
    }
    
    return 0;
}

7.差分数组

定义:给定数组 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an,若存在数组 b 1 , b 2 , ⋯   , b n b_1, b_2, \cdots, b_n b1,b2,,bn,使得数组 { a n } \{a_n\} {an} 是数组 { b n } \{b_n\} {bn} 的前缀和,即 ∀ 1 ≤ i ≤ n \forall 1 \le i \le n ∀1in,都有 a i = b 1 + b 2 + ⋯ + b i a_i = b_1 + b_2 + \cdots +b_i ai=b1+b2++bi 成立,则称 { b n } \{b_n\} {bn} { a n } \{a_n\} {an} 的差分数组。

一维差分数组的构造方法:只需令 b 1 = a 1 b_1 = a_1 b1=a1 b 2 = a 2 − a 1 b_2=a_2-a_1 b2=a2a1 ⋯ \cdots b n = a n − a n − 1 b_n=a_n-a_{n-1} bn=anan1 即可。

差分数组的作用1:若要对原数组 { a n } \{a_n\} {an} 在某个区间 [ l , r ] [l,r] [l,r] 内的所有元素都加上一个数 c c c,则利用差分数组,只需在 O ( 1 ) O(1) O(1) 时间内即可完成。其具体做法是令 b l ← b l + c b_l \leftarrow b_l + c blbl+c 同时令 b r + 1 ← b r + 1 − c b_{r+1} \leftarrow b_{r+1} - c br+1br+1c。证明如下:
a l = b 1 + b 2 + ⋯ + b l a l + 1 = b 1 + b 2 + ⋯ + b l + b l + 1 ⋯ a r = b 1 + b 2 + ⋯ + b l + b l + 1 + ⋯ + b r a r + 1 = b 1 + b 2 + ⋯ + b l + b l + 1 + ⋯ + b r + b r + 1 \begin{aligned} & a_l = b_1 + b_2 + \cdots + b_l \\ & a_{l+1} = b_1 + b_2 + \cdots + b_l + b_{l+1} \\ & \cdots \\ & a_r =b_1 + b_2 + \cdots + b_l + b_{l+1}+\cdots + b_r \\ & a_{r+1} =b_1 + b_2 + \cdots + b_l + b_{l+1}+\cdots + b_r + b_{r+1} \\ \end{aligned} al=b1+b2++blal+1=b1+b2++bl+bl+1ar=b1+b2++bl+bl+1++brar+1=b1+b2++bl+bl+1++br+br+1
若让 b l b_l bl 变成 b l + c b_{l}+c bl+c,同时让 b r + 1 b_{r+1} br+1 变成 b r + 1 − c b_{r+1}-c br+1c,则数组 { a n } \{a_n\} {an} 在区间 [ l , r ] [l,r] [l,r] 内的所有元素都将加 c c c

差分数组的作用2:若差分数组已知,则只需对差分数组求一遍前缀和,即可在 O ( n ) O(n) O(n) 时间内得到原数组。

一维差分代码:

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int a[N], b[N]; // 原数组,差分数组

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

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

7-2. 二维差分

{ a i j } \{a_{ij}\} {aij} 是原数组, { b i j } \{b_{ij}\} {bij} 是其差分数组,如果想让区域 ( x 1 , y 1 ) (x_1, y_1) (x1,y1)~ ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 内所有的 a i j a_{ij} aij 都加上一个数 c c c,则需同时进行以下四步操作:
b x 1 , y 1 ← b x 1 , y 1 + c ; / / 该操作会使(x1,y1)右下角的所有元素a都加上c b x 2 + 1 , y 1 ← b x 2 + 1 , y 1 − c ; b x 1 , y 2 + 1 ← b x 1 , y 2 + 1 − c ; b x 2 + 1 , y 2 + 1 ← b x 2 + 1 , y 2 + 1 + c . \begin{aligned} & b_{x_1, y_1} \leftarrow b_{x_1, y_1} + c; //\text{该操作会使(x1,y1)右下角的所有元素a都加上c}\\ & b_{x_2+1, y_1} \leftarrow b_{x_2+1, y_1}-c; \\ & b_{x_1, y_2+1} \leftarrow b_{x_1, y_2+1}-c; \\ & b_{x_2+1, y_2+1} \leftarrow b_{x_2+1, y_2+1} + c. \end{aligned} bx1,y1bx1,y1+c;//该操作会使(x1,y1)右下角的所有元素a都加上cbx2+1,y1bx2+1,y1c;bx1,y2+1bx1,y2+1c;bx2+1,y2+1bx2+1,y2+1+c.
二维差分代码:

#include <iostream>

using namespace std;

const int N = 1010;

int a[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()
{
    int n, m, q;
    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, j, i, 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, y1, x2, y2, c);
    }
    
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
            printf("%d ", b[i][j]);
            puts(""); // puts()函数会自动输出换行符
    }
        
    
    return 0;
}

8-1.最长连续不重复子序列

/* 双指针算法的通用模板 */
for (int i = 0, j = 0; i < n; i++)
{
    while (j < i && check(i, j)) j++;
    
    // 每道题目的具体逻辑
}
// 后记:双指针算法的时间复杂度是O(n),因为每个指针至多扫描整个序列一次,所以时间复杂度加起来是O(2n),即O(n)。

最长连续不重复子序列代码:

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int n;
int a[N], s[N]; // s[N]表示的是区间[j,i]内每个数出现的次数

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    
    int res = 0;
    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;
}

8-2. 数组元素的目标和

(从暴力解法优化成双指针解法其实就是就是找单调性。)

#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++) scanf("%d", &a[i]);
    for (int i = 0; i < m; i++) scanf("%d", &b[i]);
    
    // 对每一个i,都寻找这样的一个j,使得 a[i] + b[j] >= x,而且j要取最靠左的一个。
    // 如此,j便具有了单调性:当i逐渐增大时,j必单调递减。
    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;
}

8-3. 判断子序列:

#include <iostream>
#include <string>

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 i = 0; i < m; i++) scanf("%d", &b[i]);
    
    string res = "No";
    for (int i = 0, j = 0; i < m; i++)
    {
        if (a[j] == b[i]) j++;
        
        if (j == n)
        {
            res = "Yes";    
            break;
        }
    }
    
    cout << res << endl;
    
    return 0;
}

9.二进制中 1 的个数:
(1)n 的二进制表示中第 k 位是几?(个位是第 0 位,十位是第 1 位,。。。)

思路:

  • step 1: 先把第 k 位移到最后一位:n >> k
  • step 2: 看个位是几:x & 1

把以上两步结合到一块,即为:(n >> k) & 1(括号可以不加)。

(2)lowbit操作(是树状数组的基本操作),作用是:返回 x 的最后一位 1.

e.g. x = 1010, lowbit(x) = 10

e.g. 2. x = 1010000, lowbit(x) = 10000

在 C++ 中,-x = ~x + 1,故 lowbit 的核心操作为:x & -x

lowbit 的一个应用:统计一个数的二进制表示中 1 的个数。代码如下:

int lowbit(int x)
{
    return x & -x;
}

int res = 0;
while (x) // x是要统计的数
{
    x -= lowbit(x); // 每次都减掉二进制中的最后一个1
    res++;
}

cout << res;

二进制中 1 的个数代码如下:

#include <iostream>

using namespace std;

int lowbit(int x)
{
    return x & -x;
}

int main()
{
    int n;
    cin >> n;
    
    while (n--)
    {
        int x;
        scanf("%d", &x);
        
        int res = 0;
        while (x)
        {
            x -= lowbit(x);
            res++;
        }
        
        cout << res << ' ';
    }
    
    return 0;
}

10.区间和:

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

using namespace std;

typedef pair<int, int> PII;

const int N = 3e5 + 10;

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

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

int find(int x)
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    
    return r + 1; // 返回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), r = find(item.second);
        cout << s[r] - s[l - 1] << endl;
    }
    
    return 0;
}

11.区间合并:

注意以下问题:

  • 合并之前要先排序。
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

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

int main()
{
    int n;
    cin >> n;
    
    vector<PII> segs;
    while (n--)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        segs.push_back({l, r});
    }
    
    merge(segs);
    
    cout << segs.size() << endl;
    
    return 0;
}
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值