CodeForces 1837F Editorial for Two
这是一个常规 \(\log^2\) 做法。
最大值最小,想到二分答案。
设二分一个 \(x\),枚举分割点,就相当于要求前缀和后缀选出若干个元素,使得和均 \(\le x\),并且选数的数量 \(\ge m\)。
显然贪心选,也就是前缀和后缀都选最小的。设 \(f_i\) 为 \([1, i]\) 的前缀,选出第 \([1, j]\) 小的数,和 \(\le x\),\(j\) 的最大值。这个随便树状数组上二分。同理设 \(g_i\) 表示 \([i, n]\) 的后缀的答案。最后判是否 \(\exists i \in [0, n], f_i + g_{i + 1} \ge m\) 即可。
时间复杂度 \(O(n \log n (\log n + \log V))\),其中 \(V\) 为值域。不知道为什么还要卡常才勉强能过。
code
// Problem: F. Editorial for Two
// Contest: Codeforces - Educational Codeforces Round 149 (Rated for Div. 2)
// URL: https://codeforces.com/group/bXwyZVR0kC/contest/1837/problem/F
// Memory Limit: 256 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 300100;
ll n, m, a[maxn], b[maxn], p[maxn], c[maxn];
int f[maxn], g[maxn], d[maxn];
struct node {
ll x, i;
} e[maxn];
bool cmp(node a, node b) {
return a.x < b.x;
}
inline bool check(ll x) {
for (int i = 0; i <= n + 1; ++i) {
f[i] = g[i] = c[i] = d[i] = 0;
}
ll t = 0;
for (int i = 1; i <= n; ++i) {
t += a[i];
for (int j = p[i]; j <= n; j += (j & (-j))) {
c[j] += a[i];
++d[j];
}
int k = 0;
if (t <= x) {
k = n;
f[i] = i;
} else {
ll y = x;
for (int j = 18; ~j; --j) {
if (k + (1 << j) > n) {
continue;
}
if (c[k + (1 << j)] <= y) {
k += (1 << j);
y -= c[k];
}
}
for (int j = k; j; j -= (j & (-j))) {
f[i] += d[j];
}
}
}
t = 0;
for (int i = 0; i <= n + 1; ++i) {
c[i] = d[i] = 0;
}
for (int i = n; i; --i) {
t += a[i];
for (int j = p[i]; j <= n; j += (j & (-j))) {
c[j] += a[i];
++d[j];
}
int k = 0;
if (t <= x) {
k = n;
g[i] = n - i + 1;
} else {
ll y = x;
for (int j = 18; ~j; --j) {
if (k + (1 << j) > n) {
continue;
}
if (c[k + (1 << j)] <= y) {
k += (1 << j);
y -= c[k];
}
}
for (int j = k; j; j -= (j & (-j))) {
g[i] += d[j];
}
}
}
for (int i = 0; i <= n; ++i) {
if (f[i] + g[i + 1] >= m) {
return 1;
}
}
return 0;
}
void solve() {
scanf("%lld%lld", &n, &m);
ll s = 0;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
s += a[i];
e[i].x = a[i];
e[i].i = i;
}
sort(e + 1, e + n + 1, cmp);
for (int i = 1; i <= n; ++i) {
p[e[i].i] = i;
}
ll l = 0, r = s, ans = -1;
while (l <= r) {
ll mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%lld\n", ans);
}
int main() {
int T = 1;
scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}