「Ynoi Easy Round 2016」掉进兔子洞

咋全是 \(O(N \sqrt{N})\) 的啊。

从询问入手,设三个区间公共拥有的数有 \(c\) 个,比较好发现答案为 \((r1 - l1 + 1) + (r2 - l2 + 1) + (r3 - l3 + 1) - 3c\),所以需要找到三个区间都有的数有多少个。

公共拥有这一个问题很像 bitset,但 bitset 只能处理公共拥有的数的种类有多少,但又没有其它数据结构可以处理这一类问题,结合数据范围,考虑对 bitset 进行变形。

首先 bitset 上不能只存种类,还得与个数挂钩,考虑变进制状压,每一位进制为 \(a\) 数组中某个数的出现次数,并用 bitset 上的一段区间存储,问题来了,要用变进制状压的话,bitset 上的存储顺序就必须是从某个区间左端点顺序向右存储,同一个值不能间隔着存,需要保证是一段区间,而常规的前缀和就不能处理这一问题。

面对这些棘手的问题,一般就想到 \(O(N \sqrt{N})\) 的算法,想想分块和莫队,发现莫队非常好维护一个区间变进制状压的 bitset,每次加入或删除一个位置 \(x\) 时就直接修改区间右端点并更新 \(cnt_{a_x}\) 就行。

查询不用说了,但现在还有一个问题是开 \(N\) 个 bitset 空间不够,需要 \(1192 \text{MB}\),但此题空限 \(500 \text{MB}\),我想半天没想到咋优化,瞄了眼题解,哦,我真是个**,把询问分成三组,跑三遍就行。

时间复杂度 \(O(N \sqrt{N} + \frac{N^2}{w})\),空间复杂度 \(O(\frac{N^2}{3w})\)

好良心的 Ynoi 题,没打过这么好打的 Ynoi 题。

/*
address:https://www.luogu.com.cn/problem/P4688
AC 2026/8/5 11:10
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, q;
int a[N], disc[N], m;
inline void read(int& x) {
    x = 0;
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c <= '9' && c >= '0') x = x * 10 + c - '0', c = getchar();
}
struct query {
    int l, r, id, bel;
    bool operator < (const query& o)const {
        if (bel == o.bel) return bel & 1 ? r < o.r : r > o.r;
        return bel < o.bel;
    }
}qry[N];
int siz;
bitset<N>cur;
int cnt[N];
inline void add(int x) {
    cur[a[x] + cnt[a[x]]] = 1;
    ++cnt[a[x]];
}
inline void del(int x) {
    --cnt[a[x]];
    cur[a[x] + cnt[a[x]]] = 0;
}
int ans[N / 3];
bitset<N>s[N / 3];
inline void solve(int q) {
    for (int i = 1;i <= q;++i) {
        int l1, r1, l2, r2, l3, r3;read(l1), read(r1), read(l2), read(r2), read(l3), read(r3);
        qry[i * 3 - 2] = { l1, r1, i, (l1 + siz - 1) / siz };
        qry[i * 3 - 1] = { l2, r2, i, (l2 + siz - 1) / siz };
        qry[i * 3] = { l3, r3, i, (l3 + siz - 1) / siz };
        ans[i] = r1 - l1 + 1 + r2 - l2 + 1 + r3 - l3 + 1;
    }
    sort(qry + 1, qry + q * 3 + 1);
    for (int i = 1;i <= q;++i) s[i].set();
    cur.reset();
    for (int i = 1;i <= n;++i) cnt[i] = 0;
    int l = 1, r = 0;
    for (int i = 1;i <= q * 3;++i) {
        while (l > qry[i].l) add(--l);
        while (r < qry[i].r) add(++r);
        while (l < qry[i].l) del(l++);
        while (r > qry[i].r) del(r--);
        s[qry[i].id] &= cur;
    }
    for (int i = 1;i <= q;++i) printf("%d\n", ans[i] - s[i].count() * 3);
}
int main() {
    read(n), read(q);
    siz = sqrt(n);
    for (int i = 1;i <= n;++i) read(a[i]), disc[++m] = a[i];
    sort(disc + 1, disc + m + 1);
    for (int i = 1;i <= n;++i) a[i] = lower_bound(disc + 1, disc + n + 1, a[i]) - disc;
    solve(q / 3), solve((q + 1) / 3), solve((q + 2) / 3);
    for (int i = 1;i <= n;++i) a[i] = lower_bound(disc + 1, disc + m + 1, a[i]) - disc;
    return 0;
}
posted @ 2026-08-05 11:33  keysky  阅读(0)  评论(0)    收藏  举报