BZOJ4939[Ynoi2016]掉进兔子洞(莫队+bitset)
题目链接
扯点别的
听说这是比较亲民的一道Ynoi,于是我就去摸了一下,于是一个晚上就没了……不过至少还是学到了\(bitset\)维护可重集合的方法,以及当空间开不下时分组处理询问的花操作……
解析
大佬们说容易想到bitset,那就想到bitset吧……
这种形如“几个集合共同含有的元素”的问题容易让人想\(bitset\)搞一搞,但是我们发现裸的\(bitset\)只能表示有没有,不能表示有多少,下面就是神奇的让\(bitset\)能表示有多少的方法
就这道题而言首先肯定是离散化,但是离散化时我们不去重,这样相同元素会是连续的一段,我们取第一个出现的位置作为离散化后的值,简单举个例子:1,4,2,4,4,5 => hash[1] = 0,hash[2] = 1,hash[4] = 2,hash[5] = 5
假设我们要加入一个数\(x\),它之前已经在这个集合中出现了\(cnt\)次,那么我们把\(x + cnt\)那一位置为\(1\),删除同理,即:bitset中第\(i + j\)个位置表示数字\(i\)第\(j + 1\)次出现
这样当我们把每个\(bitset\)与起来就相当于对每个数的出现次数取了个\(min\)
回到题目,假设我们知道了三个区间的\(bitset\)与起来是\(S\),那么答案显然就是\(len1 + len2 + len3 - |S| \times 3\)
我们发现\(bitset\)加入和删除都能很快处理,询问的又是区间,那么就可以上莫队试试了,莫队的时候维护一下每个数出现的次数即可
还有一点,直接开\(1e5\)个\(bitset\)肯定开不下,于是我们把询问分组,每组上一次莫队即可
分组大小可以算算,我比较懒就从\(1e4\)开始试然后到\(3e4\)的时候就过了……
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <bitset>
#include <algorithm>
#define MAXN 100003
#define belong(x) ((x) / per_block)
typedef long long LL;
struct Query {
int l, r, id;
} qry[90005];
int arr[MAXN], hash[MAXN], cnt[MAXN], N, M, per_block, idx;
std::bitset<MAXN> cur, ans[30003];
bool vis[30003];
bool operator <(const Query &q1, const Query &q2) {
return belong(q1.l) == belong(q2.l) ? q1.r < q2.r : belong(q1.l) < belong(q2.l);
}
bool cmp(const Query &q1, const Query &q2) { return q1.id < q2.id; }
int main() {
scanf("%d%d", &N, &M);
for (int i = 0; i < N; ++i) scanf("%d", arr + i), hash[i] = arr[i];
std::sort(hash, hash + N);
for (int i = 0; i < N; ++i)
arr[i] = std::lower_bound(hash, hash + N, arr[i]) - hash;
while (per_block * per_block < N) ++per_block;
while (M) {
memset(cnt, 0, sizeof cnt);
memset(vis, 0, sizeof vis);
cur.reset();
int tot = std::min(30000, M);
for (int k = 0; k < tot * 3; ++k) scanf("%d%d", &qry[k].l, &qry[k].r), qry[k].id = k / 3;
std::sort(qry, qry + tot * 3);
for (int l = 0, r = -1, i = 0; i < tot * 3; ++i) {
--qry[i].l, --qry[i].r;
while (r < qry[i].r) ++r, cur[arr[r] + cnt[arr[r]]] = 1, ++cnt[arr[r]];
while (r > qry[i].r) --cnt[arr[r]], cur[arr[r] + cnt[arr[r]]] = 0, --r;
while (l < qry[i].l) --cnt[arr[l]], cur[arr[l] + cnt[arr[l]]] = 0, ++l;
while (l > qry[i].l) --l, cur[arr[l] + cnt[arr[l]]] = 1, ++cnt[arr[l]];
if (!vis[qry[i].id]) vis[qry[i].id] = 1, ans[qry[i].id] = cur;
else ans[qry[i].id] &= cur;
}
std::sort(qry, qry + tot * 3, cmp);
for (int i = 0; i < tot; ++i) {
int p1 = i * 3, p2 = p1 + 1, p3 = p2 + 1;
printf("%d\n", qry[p1].r - qry[p1].l + qry[p2].r - qry[p2].l + qry[p3].r - qry[p3].l + 3 - (int)ans[i].count() * 3);
}
M -= tot;
}
return 0;
}
//Rhein_E