【Ynoi 2016】掉进兔子洞

Luogu P4688 掉进兔子洞

题意

给定一个长度为 n 的序列 a

m 次询问:每次询问给定三个区间,问将三个区间内同时出现的数删掉后,还剩下多少个数。每次询问独立。

数据范围与约定

1n,m1051ai109

题解

首先发现,每次询问的答案形式为:ansi=len1+len2+len33size,其中 size 为三个区间内同时出现的颜色的个数。

所以只要我们有办法求出 size 就能解决这道题。很自然的想到 bitset 维护区间内出现的颜色,求一下交集。但是我们要求的是同时出现的颜色个数,而不是颜色种数。bitset 只能记录一个颜色存在或者不存在,不能记录存在多少个。如果换成数组,又没有了除 w 的常数。

我们可以在离散化上下手,令一个颜色离散化后的值为:序列中,小于这个数的个数加 1(不加 1 也行,随便你)。其实就是离散化把去重给去掉而已。

这样有一个好处,我们假设当前颜色为 c1,下一个颜色为 c2。那么 c2c1 就是 c1 的个数。所以如果颜色 c1 出现了 cnt 次。我们可以在 bitset 上令 c1+cnt11。这里需要注意一下,我们不应该把 c1+(cnt1)1 再置为 0,而是将其保留。这样有一个好处,c1c2 之间 1 的个数,就是 c1 出现的次数。那么最后三个区间交集的 bitset 的 1 的个数,就是我们要求的 size

最后一个问题,怎么求每个区间的 bitset?考虑到这个 bitset 因为我们离散化比较特殊的原因,没办法通过区间合并来快速得到,所以不能使用分块或者线段树之类的数据结构了。所以我们考虑使用莫队。

这里有一个问题,我们开不下 1e5 个长度为 1e5 的 bitset。我们可以对问题分组,每 4e4 个问题跑一次莫队,这样我们只需要跑 1e54e4=3 次莫队即可。

代码

#include<bits/stdc++.h>
using namespace std;
/*====================*/
#define endl "\n"
/*====================*/
typedef long long lnt;
/*====================*/
const int N = 1e5 + 10;
const int M = 1e5 + 10;
const int S1 = 4e4;//对询问分组
const int S2 = 350;//对莫队分组
/*====================*/
int n, m;
int arr[N];
/*====================*/
struct Query
{
int l, r, idx;
Query(int _l = 0, int _r = 0, int _idx = 0)
{
l = _l, r = _r, idx = _idx;
}
friend bool operator<(const Query& a, const Query& b)
{
return (a.l / S2 == b.l / S2) ? (((a.l / S2) & 1) ? (a.r > b.r) : (a.r < b.r)) : (a.l < b.l);
}
}query[3 * M];
/*====================*/
int ans[M];
/*====================*/
int cnt[N];
bitset<N>temp;
bitset<N>state[S1];
void Add(int pos)
{
temp[arr[pos] + ++cnt[arr[pos]] - 1] = 1;
}
void Del(int pos)
{
temp[arr[pos] + cnt[arr[pos]]-- - 1] = 0;
}
void Mo(int idxl, int idxr)
{
int askl = (idxl - 1) * 3 + 1;
int askr = (idxr - 1) * 3 + 3;
sort(query + askl, query + askr + 1);
/*====================*/
temp.reset();
for (int i = idxl; i <= idxr; ++i)
{
state[i % S1].set();
}
memset(cnt, 0, sizeof(cnt));
/*====================*/
int l = 1, r = 0;
for (int i = askl; i <= askr; ++i)
{
while (query[i].l < l)Add(--l);
while (r < query[i].r)Add(++r);
while (l < query[i].l)Del(l++);
while (query[i].r < r)Del(r--);
state[query[i].idx % S1] &= temp;
}
/*====================*/
for (int i = idxl; i <= idxr; ++i)
{
ans[i] -= 3 * state[i % S1].count();
}
}
/*====================*/
void Solve(void)
{
do
{
cin >> n >> m;
for (int i = 1; i <= n; ++i)
{
cin >> arr[i];
}
} while (false);//读入
do
{
vector<int>lib;
for (int i = 1; i <= n; ++i)
{
lib.push_back(arr[i]);
}
sort(lib.begin(), lib.end());
for (int i = 1; i <= n; ++i)
{
arr[i] = lower_bound(lib.begin(), lib.end(), arr[i]) - lib.begin() + 1;
}
} while (false);//离散化
do
{
int idxl = 1, idxr = S1; idxr = min(idxr, m);
while (idxl <= idxr)
{
for (int i = idxl; i <= idxr; ++i)
{
for (int j = 1; j <= 3; ++j)
{
int l, r; cin >> l >> r;
query[(i - 1) * 3 + j] = Query(l, r, i);
ans[i] += r - l + 1;
}
}
Mo(idxl, idxr);
idxl += S1, idxr += S1; idxr = min(idxr, m);
}
} while (false);//莫队
do
{
for (int i = 1; i <= m; ++i)
{
cout << ans[i] << endl;
}
} while (false);//输出
}
/*====================*/
int main()
{
#ifndef ONLINE_JUDGE
freopen("IN.txt", "r+", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(NULL), cout.tie(NULL);
int T = 1; //cin >> T;
while (T--)Solve();
return 0;
}
posted @   ProtectEMmm  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示