I. Barkley II
题目大意:
给定一个有
n
n
n 个数的序列
a
a
a ,数字的大小不超过
m
m
m ,对于区间
[
l
,
r
]
[l, r]
[l,r] 它的价值为区间当中数的种类数减去
M
E
X
MEX
MEX 值。 请你输出最大价值。
( 1 ≤ n ≤ 5 × 1 0 5 ) (1 \le n \le 5 \times 10^5) (1≤n≤5×105) , ( 1 ≤ m ≤ 5 × 1 0 5 ) (1 \le m \le 5 \times 10^5) (1≤m≤5×105) ,保证总的 n n n 的大小不超过 5 × 1 0 5 5 \times 10^5 5×105 , m m m 的大小不保证。
解题思路:
我们考虑枚举
M
E
X
MEX
MEX 的值(当然并不是直接枚举),以
M
E
X
MEX
MEX 为分界线把区间分割成一个个的小区间,我们只需要计算每一段小区间的价值取
m
a
x
max
max 即为答案。
那么我们就有一个疑问:这样分割出的区间的实际
M
E
X
MEX
MEX 可能会小于我们当前枚举的
M
E
X
MEX
MEX ,这样对吗?
当然是正确的,因为我们是在枚举的过程中取
m
a
x
max
max ,上述情况一定不是最优解的情况,因此对答案不会有影响。(模拟几个样例就明白了)
怎么计算出每个小区间的价值?
问题转化成:给定一个区间,求这个区间中数字的种类数。答案是用树状数组来维护即可。
洛谷例题:HH的项链
做会这道题目就学会了。
M
E
X
MEX
MEX 怎么枚举?
提前把每个数的位置存储到二维数组
p
o
s
pos
pos 中,再枚举
M
E
X
=
a
[
i
]
MEX = a[i]
MEX=a[i] ,假设当前这个
a
[
i
]
a[i]
a[i] 的位置是
r
=
p
o
s
[
a
[
i
]
]
[
j
]
r = pos[a[i]][j]
r=pos[a[i]][j] ,那么上一个
a
[
i
]
a[i]
a[i] 的位置就是
l
=
p
o
s
[
a
[
i
]
]
[
j
−
1
]
l = pos[a[i]][j - 1]
l=pos[a[i]][j−1] ,因此这段小区间的价值就是
f
e
n
.
s
u
m
(
r
−
1
)
−
f
e
n
.
s
u
m
(
l
)
−
M
E
X
fen.sum(r - 1) - fen.sum(l) - MEX
fen.sum(r−1)−fen.sum(l)−MEX 。
代码:
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 5e5 + 10;
template<class T>
struct Fenwick {
int n;
vector<T> a;
Fenwick(int n = 0) {
init(n);
}
void init(int n) {
this->n = n;
a.assign(n, T());
}
void add(int p, T x) {
for (int i = p; i < n; i += i & -i) {
a[i] += x;
}
}
T sum(int p) {
T res = 0;
for (int i = p; i > 0; i -= i & -i) {
res += a[i];
}
return res;
}
};
vector<vector<int>> pos(N);
vector<int> pt(N), p(N), cnt(N);
void solve() {
int n, m;
cin >> n >> m;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
pos[a[i]].emplace_back(i);
cnt[a[i]] = 1;
}
int ans = -1e9;
Fenwick<int> fen(n + 1);
for (int i = 1; i <= n; i++) {
int mex = a[i];
int r = i - 1, l;
if (pos[mex][pt[mex]] == i) {
l = 0;
} else {
l = pos[mex][pt[mex]];
pt[mex]++;
}
ans = max(ans, fen.sum(r) - fen.sum(l) - mex);
if (p[mex]) {
fen.add(p[mex], -1);
}
fen.add(i, 1);
p[mex] = i;
}
for (int i = 1; i <= n; i++) {
int mex = a[i];
int r = n, l = *pos[mex].rbegin();
ans = max(ans, fen.sum(r) - fen.sum(l) - mex);
}
for (int i = 1; i <= n; i++) {
if (!cnt[i]) {
ans = max(ans, fen.sum(n) - i);
break;
}
}
cout << ans << '\n';
for (int i = 1; i <= n; i++) {
pos[a[i]].clear();
pt[a[i]] = 0;
p[a[i]] = 0;
cnt[a[i]] = 0;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}