Description
给定序列
a
=
(
a
1
,
a
2
,
⋯
,
a
n
)
a=(a_1,a_2,\cdots,a_n)
a=(a1,a2,⋯,an),有
q
q
q 个查询
(
l
,
r
)
(l,r)
(l,r).
对每个查询求
a
l
⋯
r
a_{l\cdots r}
al⋯r 的众数,若有多个取最小的.
强制在线.
真实的
l
=
(
l
′
+
lastans
−
1
)
m
o
d
n
)
+
1
,
r
=
(
r
′
+
lastans
−
1
)
m
o
d
n
)
+
1
l=(l^\prime+\textit{lastans}-1)\bmod n)+1,r=(r^\prime+\textit{lastans}-1)\bmod n)+1
l=(l′+lastans−1)modn)+1,r=(r′+lastans−1)modn)+1,若
l
>
r
l>r
l>r 则交换两者.
Limitations
1
≤
n
≤
4
×
1
0
4
1\le n\le 4\times 10^4
1≤n≤4×104
1
≤
n
≤
5
×
1
0
4
1\le n\le 5\times 10^4
1≤n≤5×104
1
≤
a
i
≤
1
0
9
1\le a_i\le 10^9
1≤ai≤109
1
≤
l
≤
r
≤
n
1\le l\le r\le n
1≤l≤r≤n
Solution
区间众数无法用线段树维护,但
n
,
m
n,m
n,m 不大,可以用分块.
首先
a
i
a_i
ai 很大,需要先离散化.
然后借助一个桶
cnt
\textit{cnt}
cnt 预处理
pre
i
,
j
\textit{pre}_{i,j}
prei,j 表示
a
i
a_i
ai 在第
1
∼
j
1\sim j
1∼j 块的出现次数.
还有
mode
i
,
j
\textit{mode}_{i,j}
modei,j 表示第
i
∼
j
i\sim j
i∼j 块的众数.
考虑查询,如果
[
l
,
r
]
[l,r]
[l,r] 在单块内或相邻块间,我们直接暴力.
否则,我们统计两端散块内的
a
i
a_i
ai 在 中间整块内 以及 两端 的出现次数,存入
cnt
\textit{cnt}
cnt 中,并求出这部分的众数.
然后查询整块间的众数,以及
[
l
,
r
]
[l,r]
[l,r] 间这个数的出现次数(整块散块分开算),然后判断并更新答案.
有几个坑:
- 算 mode i , j \textit{mode}_{i,j} modei,j 前要先继承 mode i , j − 1 \textit{mode}_{i,j-1} modei,j−1 的结果.
- 答案要转化为离散化前的.
- cnt \textit{cnt} cnt 用完要清空,可以只清空用过的部分.
取 B = n B=\sqrt n B=n,时间复杂度 O ( ( n + m ) n ) O((n+m)\sqrt n) O((n+m)n).
Code
4.09
KB
,
1.29
s
,
9.62
MB
(in
total,
C++20
with
O2)
4.09\text{KB},1.29\text{s},9.62\text{MB}\;\texttt{(in total, C++20 with O2)}
4.09KB,1.29s,9.62MB(in total, C++20 with O2)
fastio
删了
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
namespace fastio {}
using fastio::read;
using fastio::write;
struct Block {
int n, v, size, blocks;
vector<int> a, belong, L, R, cnt;
vector<vector<int>> pre, mode;
inline Block() {}
inline Block(const vector<int>& _a, int _v) : v(_v), a(_a) {
n = a.size();
size = (int)sqrt(n);
blocks = (n + size - 1) / size;
belong.resize(n);
L.resize(blocks);
R.resize(blocks);
for (int i = 0; i < blocks; i++) {
L[i] = i * size;
R[i] = std::min(L[i] + size, n) - 1;
for (int j = L[i]; j <= R[i]; j++) belong[j] = i;
}
pre.resize(blocks, vector<int>(v, 0));
mode.resize(blocks, vector<int>(blocks, -1));
cnt.resize(v);
init();
}
// Prepare
inline void init() {
for (int i = 0; i < n; i++)
for (int j = belong[i]; j < blocks; j++) pre[j][a[i]]++;
for (int i = 0; i < blocks; i++) {
clear();
for (int j = i; j < blocks; j++) {
if (j > i) mode[i][j] = mode[i][j - 1];
for (int k = L[j]; k <= R[j]; k++) {
cnt[a[k]]++;
update(mode[i][j], a[k], cnt[a[k]]);
}
}
}
clear();
}
// Update
inline void update(int& x, int y, int c) {
if (x == -1 || (c > cnt[x]) || (c == cnt[x] && y < x)) x = y;
}
// Clear
inline void clear() { cnt.assign(v, 0); }
inline void clear(int l, int r) {
for (int i = l; i <= r; i++) cnt[a[i]] = 0;
}
// Count
inline int count_block(int bl, int br, int k) {
return pre[br - 1][k] - pre[bl][k];
}
inline int count_part(int l, int r, int k) {
int res = 0;
for (int i = l; i <= r; i++) res += (a[i] == k);
return res;
}
// Add to the bucket
inline void add(int l, int r, int bl, int br) {
for (int i = l; i <= r; i++)
if (!cnt[a[i]]) cnt[a[i]] += count_block(bl, br, a[i]);
}
// Update the answer
inline void get_part(int& ans, int l, int r) {
for (int i = l; i <= r; i++) {
cnt[a[i]]++;
update(ans, a[i], cnt[a[i]]);
}
}
// Query
inline int query(int l, int r) {
const int bl = belong[l], br = belong[r];
if (br - bl <= 2) {
int ans = -1;
get_part(ans, l, r), clear(l, r);
return ans;
}
int ans = -1;
add(l, R[bl], bl, br), add(L[br], r, bl, br);
get_part(ans, l, R[bl]), get_part(ans, L[br], r);
int k = mode[bl + 1][br - 1];
int cntk = (count_block(bl, br, k) +
count_part(l, R[bl], k) +
count_part(L[br], r, k));
update(ans, k, cntk);
clear(l, R[bl]), clear(L[br], r);
return ans;
}
};
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
const int n = read<int>(), m = read<int>();
vector<int> a(n);
for (int i = 0; i < n; i++) a[i] = read<int>();
auto disc = a;
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
for (int i = 0; i < n; i++) {
a[i] = lower_bound(disc.begin(), disc.end(), a[i]) - disc.begin();
}
Block blk(a, disc.size());
for (int i = 0, l, r, lst = 0; i < m; i++) {
l = read<int>(), r = read<int>();
l = (l + lst - 1) % n;
r = (r + lst - 1) % n;
if (l > r) swap(l, r);
write(lst = disc[blk.query(l, r)]);
putchar_unlocked('\n');
}
return 0;
}