「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;
}

浙公网安备 33010602011771号