P4688 [Ynoi2016] 莫队 + bitset

本文解析了如何使用std::bitset和莫队算法解决Ynoi2016竞赛中的问题,通过离散化和区间维护统计相同值元素出现次数,达到O(n√n + n²/32)的时间复杂度。文章还提及了空间优化策略和常数段处理技巧。
摘要由CSDN通过智能技术生成
题意

传送门 P4688 [Ynoi2016] 掉进兔子洞

题解

std::bitset 维护区间元素,由于需要统计相同值的元素出现的个数,离散化时不做去重处理,那么有序数组的相邻元素 a i , a i + 1 a_i,a_{i+1} ai,ai+1 的最小索引之间的距离(同时也是离散化的值之间的距离)为 a i a_i ai 在数组中出现的个数,那么对于 c n t [ a i ] cnt[a_i] cnt[ai] a i a_i ai,只需要将 std::bitset 的 [ a i , a i + c n t [ a i ] ) [a_i,a_i+cnt[a_i]) [ai,ai+cnt[ai]) 位设为 1 1 1 即可,此时三个区间对应的 std::bitset 的交集即为所求。

用莫队算法维护求解区间 std::bitset 即可。总时间复杂度 O ( n n + n 2 / 32 ) O(n\sqrt{n}+n^2/32) O(nn +n2/32)

考虑到空间开销过大,实现上将询问分为常数段进行处理。

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
const int MAXN = 1E5 + 5, B = 3;
typedef bitset<MAXN> bt;
int N, M, A[MAXN];
int L[MAXN], R[MAXN], idx[MAXN], res[MAXN];
int cnt[MAXN], sx[MAXN], sxn, lim;
struct Query
{
    int l, r, id;
    bool operator<(const Query &o)
    {
        if (idx[l] != idx[o.l])
            return idx[l] < idx[o.l];
        return (idx[l] & 1) ? r < o.r : r > o.r;
    }
} qs[MAXN];
bt rec[MAXN / B], tmp;

void add(int i) { tmp[A[i] + cnt[A[i]]++] = 1; }

void del(int i) { tmp[A[i] + --cnt[A[i]]] = 0; }

void solve()
{
    int n = 0;
    for (int i = 0; i < lim && M--; ++i, n += 3)
    {
        rec[i].set();
        res[i] = 0;
        for (int j = 0; j < 3; ++j)
        {
            auto &q = qs[n + j];
            cin >> q.l >> q.r;
            --q.l, q.id = i;
            res[i] += q.r - q.l;
        }
    }
    sort(qs, qs + n);
    memset(cnt, 0, sizeof(cnt));
    tmp.reset();
    for (int i = 0, l = qs[0].l, r = l; i < n; ++i)
    {
        int ql = qs[i].l, qr = qs[i].r;
        while (ql < l)
            add(--l);
        while (r < qr)
            add(r++);
        while (l < ql)
            del(l++);
        while (qr < r)
            del(--r);
        rec[qs[i].id] &= tmp;
    }
    for (int i = 0; i < n / 3; ++i)
    {
        int num = rec[i].count();
        cout << res[i] - 3 * num << '\n';
    }
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> N >> M;
    for (int i = 0; i < N; ++i)
        cin >> A[i];
    int sz = ceil(sqrt(N));
    for (int l = 0, i = 0; l < N; l += sz, ++i)
    {
        L[i] = l, R[i] = min(l + sz, N);
        for (int j = L[i]; j < R[i]; ++j)
            idx[j] = i;
    }
    for (int i = 0; i < N; ++i)
        sx[sxn++] = A[i];
    sort(sx, sx + sxn);
    for (int i = 0; i < N; ++i)
        A[i] = lower_bound(sx, sx + sxn, A[i]) - sx;
    lim = M / B + 1;
    for (int i = 0; i < B; ++i)
        solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值