Bootstrap

C++计算给定序列在多次修改前后满足特定条件的极大匹配方案的大小

给定长度为n的整数序列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an和长度为n的01序列 b 1 , b 2 , . . . , b n b_1,b_2,...,b_n b1,b2,...,bn
对于 1 ≤ i < j ≤ n 1\leq i<j\leq n 1i<jn,称二元组 ( i , j ) (i,j) (i,j)构成匹配当且仅当 b i = 0 b_i=0 bi=0 b j = 1 b_j=1 bj=1
定义极大匹配方案 S m a x S_{max} Smax为满足以下所有条件的二元组集合:

  • 对于任意 ( u , v ) ∈ S m a x (u,v)\in S_{max} (u,v)Smax 1 ≤ u < v ≤ n 1\leq u<v\leq n 1u<vn ( u , v ) (u,v) (u,v)构成匹配;
  • 每一个整数 1 ≤ i ≤ n 1\leq i\leq n 1in S m a x S_{max} Smax中出现至多一次;
  • 在满足以上条件的前提下,满足 a u = a v a_u=a_v au=av的二元组 ( u , v ) (u,v) (u,v)数量最多,即 ∑ ( u , v ) ∈ S m a x [ a u = a v ] \sum_{(u,v)\in S_{max}}[a_u=a_v] (u,v)Smax[au=av]最大;
  • 在满足以上条件的前提下, ∣ S m a x ∣ |S_{max}| Smax最大。
    给定m次修改,每次修改给出 x , p , q x,p,q x,p,q,表示将 ( a x , b x ) (a_x,b_x) (ax,bx)修改为 ( p , q ) (p,q) (p,q)
    需要用C++实现对于每个 1 ≤ i ≤ m + 1 1\leq i\leq m+1 1im+1求出:按输入顺序依次进行前 ( i − 1 ) (i-1) (i1)次操作后,极大匹配方案 S m a x S_{max} Smax的大小 ∣ S m a x ∣ |S_{max}| Smax

输入格式
从标准输入读入数据。
输入的第一行两个整数n,m,表示序列长度和操作次数。
第二行n个整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an描述序列a。
第三行n个整数 b 1 , b 2 , . . . , b n b_1,b_2,...,b_n b1,b2,...,bn描述序列b。
接下来m行每行三个整数x、p、q,描述一次修改。
输出格式
输出到标准输出。
输出m+1行,每行一个整数,第i行表示按输入顺序依次进行前 ( i − 1 ) (i-1) (i1)次操作后 ∣ S m a x ∣ |S_{max}| Smax的值。
样例输入

5 5
1 2 1 1 2
0 0 0 0 0
2 2 1
4 2 0
4 2 1
2 2 0
1 1 1

样例1的输出
- 输出内容为:

0
1
1
2
1
1
  • 样例1的解释
    - 初始情况,由于所有的 ( b_i ) 都等于 0,因此没有二元组构成匹配,极大匹配方案的大小为 0,故第一行输出 0。
    - 进行第一次修改后,( b_2 = 1 ),极大匹配方案为 ({ (1, 2) }),故第二行输出 1。
    - 进行前三次修改后,序列 ( a ) 为 1 2 1 2 2,序列 ( b ) 为 0 1 0 1 0。极大匹配方案为 ({ (1, 2), (3, 4) }),故第四行输出 2。注意此时 ((4, 5)) 有 ( b_4 = 1 ),( b_5 = 0 ),并不构成匹配。

样例2的输入
- 输入内容为:

10 10
2 1 2 2 2 2 1 2 2 2
1 1 0 0 0 0 1 0 0 0
3 2 0
5 1 0
9 1 1
4 2 1
8 1 1
2 1 0
1 2 1
8 2 0
1 1 1
9 1 0

样例2的输出

1
1
1
2
3
3
4
4
3
3
2

【子任务】
对于所有测试数据,
1 ≤ n ≤ 2 × 1 0 5 , 0 ≤ m ≤ 2 × 1 0 5 ; 1 \leq n \leq 2\times 10^5,0 \leq m \leq 2\times 10^5; 1n2×105,0m2×105;
对于 1 ≤ i ≤ n , 1 ≤ a i ≤ n , 0 ≤ b i ≤ 1 1 \leq i \leq n,1 \leq a_i \leq n,0 \leq b_i \leq 1 1in,1ain,0bi1;
每次修改中有 1 ≤ x ≤ n , 1 ≤ p ≤ n , 0 ≤ q ≤ 1 1 \leq x \leq n, 1 \leq p \leq n, 0 \leq q \leq 1 1xn,1pn,0q1
Subtask 1(10%): n ≤ 100 , m = 0 n \leq 100,m=0 n100,m=0
Subtask 2(15%): n ≤ 2 × 1 0 3 , m = 0 n \leq 2 \times 10^3,m=0 n2×103,m=0
Subtask 3(20%): m = 0 m=0 m=0
Subtask 4(15%): a i , p ≤ 2 a_i,p \leq 2 ai,p2
Subtask 5(20%): n , m ≤ 1 0 5 n,m \leq 10^5 n,m105
Subtask 6(20%):无特殊限制。

为了解决这个问题,我们需要找到一种高效的方法来计算在每次修改后的极大匹配方案的大小。这个问题的关键在于如何在动态修改后快速计算满足条件的配对数目。

这种方法通过动态维护每个值的0和1的数量,高效地计算每次修改后的极大匹配方案大小,确保了在O(1)时间复杂度内完成每次修改后的结果计算。

方法思路

  1. 问题分析:

    • 我们需要处理两个序列,一个整数序列和一个01序列。
    • 每次修改后,我们需要计算极大匹配方案的大小,该方案需要优先考虑相同值的配对,并且在满足该条件后尽可能多的配对。
  2. 关键观察:

    • 每个值a的0和1的数量决定了该值的有效配对数目。
    • 使用两个字典来维护每个值的0和1的数量。
    • 维护总0和总1的数量,以及一个变量来记录所有值的有效配对数目总和。
  3. 算法步骤:

    • 初始化时统计每个值的0和1的数量,以及总0和总1的数量。
    • 每次修改时更新相关值的信息,并动态维护有效配对数目总和。
    • 计算剩余0和1的数量,并基于这些剩余数量计算最终的配对数目。

解决代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;

    vector<int> a(n);
    vector<int> b(n);

    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }

    for (int i = 0; i < n; ++i) {
        cin >> b[i];
    }

    unordered_map<int, int> c0, c1;
    int total0 = 0, total1 = 0;
    long long sum_pair = 0;

    auto update = [&](int old_a, int old_b, int delta) {
        sum_pair -= min(c0[old_a], c1[old_a]);
        if (old_b == 0) {
            c0[old_a] += delta;
            if (c0[old_a] == 0) c0.erase(old_a);
        } else {
            c1[old_a] += delta;
            if (c1[old_a] == 0) c1.erase(old_a);
        }
        sum_pair += min(c0[old_a], c1[old_a]);
    };

    for (int i = 0; i < n; ++i) {
        int val = a[i];
        int bit = b[i];
        if (bit == 0) {
            c0[val]++;
            total0++;
        } else {
            c1[val]++;
            total1++;
        }
    }

    for (auto it = c0.begin(); it != c0.end();) {
        int key = it->first;
        sum_pair += min(it->second, c1[key]);
        ++it;
    }
    for (auto it = c1.begin(); it != c1.end();) {
        int key = it->first;
        if (c0.find(key) == c0.end()) {
            sum_pair += min(0, it->second);
        }
        ++it;
    }

    cout << sum_pair + min(total0 - sum_pair, total1 - sum_pair) << '\n';

    for (int i = 0; i < m; ++i) {
        int x, p, q;
        cin >> x >> p >> q;
        x--;

        int old_a = a[x];
        int old_b = b[x];
        int new_a = p;
        int new_b = q;

        sum_pair -= min(c0[old_a], c1[old_a]);
        if (old_b == 0) {
            c0[old_a]--;
            total0--;
            if (c0[old_a] == 0) c0.erase(old_a);
        } else {
            c1[old_a]--;
            total1--;
            if (c1[old_a] == 0) c1.erase(old_a);
        }
        sum_pair += min(c0[old_a], c1[old_a]);

        sum_pair -= min(c0[new_a], c1[new_a]);
        if (new_b == 0) {
            c0[new_a]++;
            total0++;
        } else {
            c1[new_a]++;
            total1++;
        }
        sum_pair += min(c0[new_a], c1[new_a]);

        a[x] = new_a;
        b[x] = new_b;

        long long rest0 = total0 - sum_pair;
        long long rest1 = total1 - sum_pair;
        long long ans = sum_pair + min(rest0, rest1);
        cout << ans << '\n';
    }

    return 0;
}

代码解释

  • 初始化: 读取输入并初始化每个值的0和1的数量,以及总0和总1的数量。
  • 更新函数: 用于动态维护每个值的0和1的数量变化,并更新有效配对数目总和。
  • 处理初始状态: 计算初始的有效配对数目总和,并输出初始结果。
  • 处理每次修改: 更新相关值的信息,重新计算有效配对数目总和,并输出当前结果。
;