Luogu11994 [JOIST 2025] 外郎糕 / Uiro 做题记录
随便写点。link
先进性一定的观察,对于单个询问我得到了两个贪心做法:
-
每次先取负数,放进一个堆里,如果当前和 \(< 0\) 则把绝对值最大的数改成正数。即,反悔贪心。
-
从小到大扫描数值,能取负就尽量取负。这个可以使用调整法反证。
注意到 \(A_i \le 100\),所以正解很可能是第二个贪心做法的拓展,或者我还没想到。
先尝试这个做法,枚举到一个数值时,如果我们取负的是一个后缀,那么一定满足“尽量多”的条件。
但是这样做仍然不好拓展到多个询问的情况,可能的思路是观察性质,或者更换做法。
观察性质,发现如果对于 \(1\le x < y\le 100\),满足前面有一个 \(y\) 取负,后面有一个 \(x\) 取正,这是不可能的。
所以,对于当前枚举的数值 \(v\),我们能取负的 \(v\) 的后面的所有 \(<v\) 的数一定都取负。
也就是说,这后面的数取正还是取负是固定的,这启示我们静态地处理整个序列。
具体的,离线并统一从小到大枚举数值 \(v\)。其中 \(\le v\) 的数默认取负,\(\ge v + 1\) 的数默认取正。
对于每个询问,我们二分后缀长度,由于序列是静态的,所以可以使用 ST 表快速判定。
二分出后缀后,我们需要更新该询问的左端点为上一个取正的 \(v\) 的位置 \(+1\),因为需要保证后续二分的正确性。
时间复杂度 \(\mathcal O((n + q)a\log n)\)。
点击查看代码
#include <bits/stdc++.h>
#define ll int
#define LL long long
#define uLL unsigned LL
#define fi first
#define se second
#define mkp make_pair
#define pir pair<ll, ll>
#define pb push_back
#define i128 __int128
using namespace std;
char buf[1 << 22], *p1, *p2;
template <class T>
const inline void rd(T &x) {
char ch;
bool neg = 0;
while (!isdigit(ch = getchar()))
if (ch == '-')
neg = 1;
x = ch - '0';
while (isdigit(ch = getchar())) x = (x << 1) + (x << 3) + ch - '0';
if (neg)
x = -x;
}
const ll maxn = 2e5 + 10, mod = 1e9 + 7, M = 1e4 + 10, inf = 1e9;
ll power(ll a, ll b = mod - 2, ll p = mod) {
ll s = 1;
while (b) {
if (b & 1)
s = 1ll * s * a % p;
a = 1ll * a * a % p, b >>= 1;
}
return s;
}
template <class T, class _T>
const inline ll pls(const T x, const _T y) { return x + y >= mod ? x + y - mod : x + y; }
template <class T, class _T>
const inline ll mus(const T x, const _T y) { return x < y ? x + mod - y : x - y; }
template <class T, class _T>
const inline void add(T &x, const _T y) { x = x + y >= mod ? x + y - mod : x + y; }
template <class T, class _T>
const inline void sub(T &x, const _T y) { x = x < y ? x + mod - y : x - y; }
template <class T, class _T>
const inline void chkmax(T &x, const _T y) { x = x < y ? y : x; }
template <class T, class _T>
const inline void chkmin(T &x, const _T y) { x = x < y ? x : y; }
ll n, m, a[maxn], ql[maxn], qr[maxn], ans[maxn], s[maxn], pre[maxn];
ll st[20][maxn], Log[maxn], cnt[maxn], b[maxn];
ll ask(ll l, ll r) {
ll k = Log[r - l + 1];
return min(st[k][l], st[k][r - (1 << k) + 1]);
}
int main() {
rd(n);
for(ll i = 1; i <= n; i++) rd(a[i]);
rd(m);
for(ll i = 1; i <= m; i++) rd(ql[i]), rd(qr[i]);
for(ll i = 2; i <= n; i++) Log[i] = Log[i >> 1] + 1;
for(ll i = 1; i <= 100; i++) {
for(ll j = 1; j <= n; j++) {
cnt[j] = cnt[j - 1] + (a[j] == i);
pre[j] = pre[j - 1] + a[j] - 2 * (a[j] <= i? a[j] : 0);
st[0][j] = pre[j];
if(a[j] == i) b[cnt[j]] = j;
}
for(ll j = 1; (1 << j) <= n; j++)
for(ll k = 1; k + (1 << j) - 1 <= n; k++)
st[j][k] = min(st[j - 1][k], st[j - 1][k + (1 << j - 1)]);
for(ll j = 1; j <= m; j++) {
ll lo = cnt[ql[j] - 1] + 1, hi = cnt[qr[j]], t = lo;
while(lo <= hi) {
ll mid = lo + hi >> 1;
if(ask(b[mid], qr[j]) - pre[ql[j] - 1] +
s[j] + (mid - t) * 2 * i >= 0) hi = mid - 1;
else lo = mid + 1;
}
ll c = lo;
ans[j] += cnt[qr[j]] - c + 1;
if(c > t) {
ll x = b[c - 1];
s[j] += pre[x] - pre[ql[j] - 1] + 2 * (c - t) * i;
ql[j] = x + 1;
}
}
}
for(ll i = 1; i <= m; i++) printf("%d\n", ans[i]);
return 0;
}

浙公网安备 33010602011771号