AtCoder Beginner Contest 242 G - Range Pairing Query (莫队)

每周五篇博客:(5/5) 我做到了!

https://atcoder.jp/contests/abc242/tasks/abc242_g

这题主要是想给大家提供一份莫队的板子,很多莫队题基本上填空就差不多了(

板子

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) std::cin >> a[i];

    int q;
    std::cin >> q;
    std::vector<std::array<int, 3>> que(q);
    for (int i = 0; i < q; i ++) {
        std::cin >> que[i][0] >> que[i][1];
        que[i][2] = i;
    }

    const int M = 300;
    std::sort(que.begin(), que.end(), [](std::array<int, 3> a, std::array<int, 3> b) {
        if (a[0] / M == b[0] / M) return a[1] < b[1];
        return a[0] / M < b[0] / M;
    });

    std::vector<int> ans(q);
    i64 res = 0;
    auto add = [&](int i) {
    };
    auto del = [&](int i) {
    };

    int L = 1, R = 0;
    for (auto [l, r, id] : que) {
        if (l == r) {
            continue;
        }
        while (L > l) L --, add(L);
        while (R < r) R ++, add(R);
        while (L < l) del(L), L ++;
        while (R > r) del(R), R --;
        ans[id] = res;
    }

    for (auto i : ans) std::cout << i << '\n';
    std::cout << '\n';
}

题意

N N N 人编号 1 , 2 , … , N 1,2,\dots,N 1,2,,N 连续站立。人 i i i 穿颜色 A i A_i Ai

回答以下格式的查询 Q Q Q

  • 给您整数 l l l r r r 。考虑到只有一个人 l , l + 1 , … , r l,l+1,\dots,r l,l+1,,r ,最多可以形成几对穿着相同颜色的人?

思路

事实上这就是莫队板子题,简单说一下莫队

莫队可以把可离线询问的题目转化为一个 O ( n n ) O(n\sqrt n) O(nn ) 时间复杂度

值得注意的是在移动左右端点时,我们应该先扩展区间再缩小区间

具体一点的可以看oiwiki-普通莫队算法

了解莫队后,我们只需要在上面的板子进行一些魔改/填空

我们维护一个数组 c n t i cnt_i cnti,表示当前莫队代表的区间的衣服颜色为 i i i 的衣服数量

定义 r e s res res 表示当前莫队代表的区间的答案

对于增加区间的元素时,如果加上这个元素后, c n t i cnt_i cnti 变成了偶数,说明我们又凑出来了一对相同的颜色衣服,所以此时 r e s ← r e s + 1 res \gets res + 1 resres+1,同时维护 r e s i res_i resi

对于增加区间的元素时,如果减去这个元素之前, c n t i cnt_i cnti 是偶数,说明我们要拆散一对相同的颜色衣服,所以此时 r e s ← r e s − 1 res \gets res - 1 resres1,同时维护 r e s i res_i resi

那么这题就解决了,真是一道典典又板板的题目呢

代码

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) std::cin >> a[i];

    int q;
    std::cin >> q;
    std::vector<std::array<int, 3>> que(q);
    for (int i = 0; i < q; i ++) {
        std::cin >> que[i][0] >> que[i][1];
        que[i][2] = i;
    }

    const int M = 300;
    std::sort(que.begin(), que.end(), [](std::array<int, 3> a, std::array<int, 3> b) {
        if (a[0] / M == b[0] / M) return a[1] < b[1];
        return a[0] / M < b[0] / M;
    });

    std::vector<int> ans(q);
    std::vector<int> cnt(N);
    i64 res = 0;
    auto add = [&](int i) {
        cnt[a[i]] ++;
        if (cnt[a[i]] % 2 == 0) res ++;
    };
    auto del = [&](int i) {
        if (cnt[a[i]] % 2 == 0) res --;;
        cnt[a[i]] --;
    };

    int L = 1, R = 0;
    for (auto [l, r, id] : que) {
        if (l == r) {
            continue;
        }
        while (L > l) L --, add(L);
        while (R < r) R ++, add(R);
        while (L < l) del(L), L ++;
        while (R > r) del(R), R --;
        ans[id] = res;
    }

    for (auto i : ans) std::cout << i << '\n';
    std::cout << '\n';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值