AtCoder Regular Contest 168 F Up-Down Queries
貌似是第三道问号题?感觉前面这个转化不是人能想到的。。。
考虑维护 \(y\) 的差分序列。更进一步地,我们类比 slope trick,维护一个可重集,里面有 \(y_{i + 1} - y_i\) 个 \(i\)(为了方便我们让每次操作时 \(y_{m + 1}\) 加 \(1\))。那么一次操作就相当于,插入两个 \(a_i\),删去最小值。
考虑统计答案。如果不考虑 \(\max\) 操作就是 \(\sum\limits_{i = 1}^n m - 2a_i\)。考虑 \(\max\) 操作,相当于每次有最小值个数避免了减 \(1\)。所以答案每次再加上最小值。
于是求 \(f(a)\) 可以转化为:从左往右遍历 \(a\),往可重集中加入 \(2\) 个 \(a_i\),把此时可重集的最小值累加进答案,再删除最小值。
考虑倒着操作,转化为从右往左遍历 \(a\),往可重集中加入 \(2\) 个 \(a_i\),删除可重集中的最大值。最后可重集中的数的和就是答案。
这样就转化成了一个比较标准的 P9168 [省选联考 2023] 人员调度 的链的问题了。简单讲一下这个问题的做法。
考虑初始时一个数也没有,然后我们在一些位置加数。加数的同时维护最后还在可重集中的数。
设当前要加的数的位置是 \(u\),数值是 \(x\)。我们找到 \(u\) 的祖先即 \([1, u]\) 中最后一个满的位置 \(v\)(一个点 \(v\) 满了指 \(v\) 的子树即 \([v, n]\) 中数的个数 \(= sz_v = n - v + 1\))。
如果 \(v\) 不存在我们放心把这个数加入即可,因为不会造成 pop。
否则,我们找到 \(v\) 的子树中的最大值,设其权值为 \(w\)。
若 \(x > w\),那么 \(x\) 加入后在 \(v\) 位置一定会被 pop,所以不用加入。
否则,因为 \(w\) 不会被 pop,所以 \(x\) 也不会被 pop。于是我们直接用 \(x\) 替换 \(w\) 即可。
注意到我们不能删除,考虑线段树分治变删除为撤销即可。
实现时需要一棵线段树维护每个位置的 \(sz\) 减去其子树内数的个数,一棵线段树维护子树中所有数的最大值,配合 \(n\) 个可重集维护每个点上的数。
时间复杂度 \(O(n \log^2 n)\),需要卡常。卡常主要两点,第一棵线段树不要用 pair 维护最小值及其位置,改成线段树上二分;注意到可重集中至多两个元素,所以可以手写。
卡常前的代码
// Problem: F - Up-Down Queries
// Contest: AtCoder - ALGO ARTIS Programming Contest 2023 Autumn(AtCoder Regular Contest 168)
// URL: https://atcoder.jp/contests/arc168/tasks/arc168_f
// Memory Limit: 1024 MB
// Time Limit: 5000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;
const int maxn = 500100;
const int inf = 0x3f3f3f3f;
int n, m, q, b[maxn], tot, c[maxn];
ll ans[maxn], res;
vector<int> vc[maxn << 2];
multiset<pii> S[maxn];
struct node {
int x, y;
node(int a = 0, int b = 0) : x(a), y(b) {}
} a[maxn];
void update(int rt, int l, int r, int ql, int qr, int x) {
if (ql > qr) {
return;
}
if (ql <= l && r <= qr) {
vc[rt].pb(x);
vc[rt].pb(x);
return;
}
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
}
struct {
pii a[maxn << 2];
int tag[maxn << 2];
inline pii get(pii a, pii b) {
return (a.fst < b.fst || (a.fst == b.fst && a.scd > b.scd)) ? a : b;
}
inline void pushup(int x) {
a[x] = get(a[x << 1], a[x << 1 | 1]);
}
inline void pushdown(int x) {
if (!tag[x]) {
return;
}
a[x << 1].fst += tag[x];
a[x << 1 | 1].fst += tag[x];
tag[x << 1] += tag[x];
tag[x << 1 | 1] += tag[x];
tag[x] = 0;
}
void build(int rt, int l, int r) {
if (l == r) {
a[rt] = mkp(n - l + 1, l);
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushup(rt);
}
void update(int rt, int l, int r, int ql, int qr, int x) {
if (ql <= l && r <= qr) {
a[rt].fst += x;
tag[rt] += x;
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
pushup(rt);
}
pii query(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return a[rt];
}
pushdown(rt);
int mid = (l + r) >> 1;
pii res = mkp(inf, 0);
if (ql <= mid) {
res = get(res, query(rt << 1, l, mid, ql, qr));
}
if (qr > mid) {
res = get(res, query(rt << 1 | 1, mid + 1, r, ql, qr));
}
return res;
}
} t1;
struct {
pii a[maxn << 2];
inline void pushup(int x) {
a[x] = max(a[x << 1], a[x << 1 | 1]);
}
void build(int rt, int l, int r) {
a[rt].fst = -inf;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void update(int rt, int l, int r, int x, pii y) {
if (l == r) {
a[rt] = y;
return;
}
int mid = (l + r) >> 1;
(x <= mid) ? update(rt << 1, l, mid, x, y) : update(rt << 1 | 1, mid + 1, r, x, y);
pushup(rt);
}
pii query(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return a[rt];
}
int mid = (l + r) >> 1;
pii res = mkp(-inf, 0);
if (ql <= mid) {
res = max(res, query(rt << 1, l, mid, ql, qr));
}
if (qr > mid) {
res = max(res, query(rt << 1 | 1, mid + 1, r, ql, qr));
}
return res;
}
} t2;
void dfs(int rt, int l, int r) {
vector<pii> v1, v2;
ll lstres = res;
for (int i : vc[rt]) {
int u = a[i].x, x = a[i].y;
pii p = t1.query(1, 1, n, 1, u);
// printf("u, x: %d %d\n", u, x);
if (p.fst) {
// printf("%d %d\n", u, x);
v1.pb(u, -1);
v2.pb(i, 1);
t1.update(1, 1, n, 1, u, -1);
S[u].emplace(x, i);
res += x;
t2.update(1, 1, n, u, *(--S[u].end()));
continue;
}
int v = p.scd;
// printf("v: %d\n", v);
p = t2.query(1, 1, n, v, n);
int w = p.scd;
if (x < a[w].y) {
// printf("%d -%d\n", x, a[w].y);
res += x - a[w].y;
t1.update(1, 1, n, 1, u, -1);
t1.update(1, 1, n, 1, a[w].x, 1);
v1.pb(u, -1);
v1.pb(a[w].x, 1);
S[a[w].x].erase(S[a[w].x].find(mkp(a[w].y, w)));
t2.update(1, 1, n, a[w].x, *(--S[a[w].x].end()));
S[u].emplace(x, i);
t2.update(1, 1, n, u, *(--S[u].end()));
v2.pb(w, 0);
v2.pb(i, 1);
}
}
if (l == r) {
// printf("res: %lld\n", res);
ans[l] += res;
} else {
int mid = (l + r) >> 1;
dfs(rt << 1, l, mid);
dfs(rt << 1 | 1, mid + 1, r);
}
res = lstres;
reverse(v1.begin(), v1.end());
reverse(v2.begin(), v2.end());
for (pii p : v1) {
t1.update(1, 1, n, 1, p.fst, -p.scd);
}
for (pii p : v2) {
int i = p.fst;
if (p.scd) {
S[a[i].x].erase(S[a[i].x].find(mkp(a[i].y, i)));
} else {
S[a[i].x].emplace(a[i].y, i);
}
t2.update(1, 1, n, a[i].x, *(--S[a[i].x].end()));
}
}
void solve() {
scanf("%d%d%d", &n, &m, &q);
ll s = 0;
for (int i = 1; i <= n; ++i) {
b[i] = i;
a[i].x = i;
S[i].emplace(-inf, 0);
scanf("%d", &a[i].y);
s += m - a[i].y * 2;
}
tot = n;
for (int i = 1, x, y; i <= q; ++i) {
scanf("%d%d", &x, &y);
s -= m - a[b[x]].y * 2;
update(1, 1, q, max(c[x], 1), i - 1, b[x]);
c[x] = i;
a[++tot] = node(x, y);
b[x] = tot;
s += m - a[b[x]].y * 2;
ans[i] = s;
}
for (int i = 1; i <= n; ++i) {
update(1, 1, q, max(c[i], 1), q, b[i]);
}
t1.build(1, 1, n);
t2.build(1, 1, n);
dfs(1, 1, q);
for (int i = 1; i <= q; ++i) {
printf("%lld\n", ans[i]);
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
卡常后的代码
// Problem: F - Up-Down Queries
// Contest: AtCoder - ALGO ARTIS Programming Contest 2023 Autumn(AtCoder Regular Contest 168)
// URL: https://atcoder.jp/contests/arc168/tasks/arc168_f
// Memory Limit: 1024 MB
// Time Limit: 5000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;
namespace IO {
char ibuf[1 << 20], *iS, *iT, obuf[1 << 20], *oS = obuf;
#define writesp(x) write(x), pc(' ')
#define writeln(x) write(x), pc('\n')
#if ONLINE_JUDGE
#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, 1 << 20, stdin), (iS == iT ? EOF : *iS++) : *iS++)
#else
#define gh() getchar()
#endif
template<typename T = int>
inline T read() {
char ch = gh();
T x = 0;
bool t = 0;
while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
return t ? ~(x - 1) : x;
}
inline void flush () {
fwrite(obuf, 1, oS - obuf, stdout), oS = obuf;
}
inline void pc (char ch) {
if (oS == obuf + (1 << 20)) flush();
*oS++ = ch;
}
template<typename _Tp>
inline void write (_Tp x) {
static char stk[64], *tp = stk;
if (x < 0) x = ~(x - 1), pc('-');
do *tp++ = x % 10, x /= 10;
while (x);
while (tp != stk) pc((*--tp) | 48);
}
}
using IO::read;
using IO::write;
using IO::pc;
using IO::flush;
const int maxn = 500100;
const int inf = 0x3f3f3f3f;
int n, m, q, b[maxn], tot, c[maxn];
ll ans[maxn], res;
vector<int> vc[maxn << 2];
db tim;
struct {
pii x, y;
inline void init() {
x = y = mkp(-inf, 0);
}
inline void insert(pii p) {
(x.fst == -inf) ? (x = p) : (y = p);
}
inline void erase(pii p) {
(x == p ? x : y) = mkp(-inf, 0);
}
inline pii query() {
return max(x, y);
}
} S[maxn];
struct node {
int x, y;
node(int a = 0, int b = 0) : x(a), y(b) {}
} a[maxn];
void update(int rt, int l, int r, int ql, int qr, int x) {
if (ql > qr) {
return;
}
if (ql <= l && r <= qr) {
vc[rt].pb(x);
vc[rt].pb(x);
return;
}
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
}
struct {
int a[maxn << 2], tag[maxn << 2];
inline void pushup(int x) {
a[x] = min(a[x << 1], a[x << 1 | 1]);
}
inline void pushdown(int x) {
if (!tag[x]) {
return;
}
a[x << 1] += tag[x];
a[x << 1 | 1] += tag[x];
tag[x << 1] += tag[x];
tag[x << 1 | 1] += tag[x];
tag[x] = 0;
}
void build(int rt, int l, int r) {
if (l == r) {
a[rt] = n - l + 1;
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushup(rt);
}
void update(int rt, int l, int r, int ql, int qr, int x) {
if (ql <= l && r <= qr) {
a[rt] += x;
tag[rt] += x;
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
pushup(rt);
}
inline int find(int rt, int l, int r) {
while (l != r) {
pushdown(rt);
int mid = (l + r) >> 1;
(a[rt << 1 | 1] == 0) ? (rt = rt << 1 | 1, l = mid + 1) : (rt <<= 1, r = mid);
}
return l;
}
int find(int rt, int l, int r, int ql, int qr) {
if (a[rt] > 0) {
return -1;
}
if (ql <= l && r <= qr) {
return find(rt, l, r);
}
pushdown(rt);
int mid = (l + r) >> 1;
if (qr > mid) {
int t = find(rt << 1 | 1, mid + 1, r, ql, qr);
if (t != -1) {
return t;
}
}
if (ql <= mid) {
int t = find(rt << 1, l, mid, ql, qr);
if (t != -1) {
return t;
}
}
return -1;
}
} t1;
struct {
pii a[maxn << 2];
inline void pushup(int x) {
a[x] = max(a[x << 1], a[x << 1 | 1]);
}
void build(int rt, int l, int r) {
a[rt].fst = -inf;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void update(int rt, int l, int r, int x, pii y) {
if (l == r) {
a[rt] = y;
return;
}
int mid = (l + r) >> 1;
(x <= mid) ? update(rt << 1, l, mid, x, y) : update(rt << 1 | 1, mid + 1, r, x, y);
pushup(rt);
}
inline void upd(int x, pii y) {
int rt = 1, l = 1, r = n;
while (1) {
a[rt] = max(a[rt], y);
if (l == r) {
return;
}
int mid = (l + r) >> 1;
(x <= mid) ? (rt <<= 1, r = mid) : (rt = rt << 1 | 1, l = mid + 1);
}
}
inline pii query(int x) {
int rt = 1, l = 1, r = n;
pii res = mkp(-inf, 0);
while (1) {
if (x <= l) {
return max(res, a[rt]);
}
int mid = (l + r) >> 1;
if (x <= mid) {
res = max(res, a[rt << 1 | 1]);
rt = rt << 1;
r = mid;
} else {
rt = rt << 1 | 1;
l = mid + 1;
}
}
}
} t2;
void dfs(int rt, int l, int r) {
vector<pii> v1, v2;
ll lstres = res;
for (int i : vc[rt]) {
int u = a[i].x, x = a[i].y;
int v = t1.find(1, 1, n, 1, u);
// printf("u, x: %d %d\n", u, x);
if (v == -1) {
// printf("%d %d\n", u, x);
v1.pb(u, -1);
v2.pb(i, 1);
t1.update(1, 1, n, 1, u, -1);
pii t = S[u].query();
S[u].insert(mkp(x, i));
res += x;
if (S[u].query() != t) {
t2.update(1, 1, n, u, S[u].query());
}
continue;
}
pii p = t2.query(v);
int w = p.scd;
if (x < a[w].y) {
// printf("%d -%d\n", x, a[w].y);
res += x - a[w].y;
t1.update(1, 1, n, 1, u, -1);
t1.update(1, 1, n, 1, a[w].x, 1);
v1.pb(u, -1);
v1.pb(a[w].x, 1);
pii t = S[a[w].x].query();
S[a[w].x].erase(mkp(a[w].y, w));
if (S[a[w].x].query() != t) {
t2.update(1, 1, n, a[w].x, S[a[w].x].query());
}
t = S[u].query();
S[u].insert(mkp(x, i));
if (S[u].query() != t) {
t2.upd(u, S[u].query());
}
v2.pb(w, 0);
v2.pb(i, 1);
}
}
if (l == r) {
// printf("res: %lld\n", res);
ans[l] += res;
} else {
int mid = (l + r) >> 1;
dfs(rt << 1, l, mid);
dfs(rt << 1 | 1, mid + 1, r);
}
res = lstres;
reverse(v1.begin(), v1.end());
reverse(v2.begin(), v2.end());
for (pii p : v1) {
t1.update(1, 1, n, 1, p.fst, -p.scd);
}
for (pii p : v2) {
int i = p.fst;
pii t = S[a[i].x].query();
if (p.scd) {
S[a[i].x].erase(mkp(a[i].y, i));
if (S[a[i].x].query() != t) {
t2.update(1, 1, n, a[i].x, S[a[i].x].query());
}
} else {
S[a[i].x].insert(mkp(a[i].y, i));
if (S[a[i].x].query() != t) {
t2.upd(a[i].x, S[a[i].x].query());
}
}
}
}
void solve() {
scanf("%d%d%d", &n, &m, &q);
ll s = 0;
for (int i = 1; i <= n; ++i) {
b[i] = i;
a[i].x = i;
S[i].init();
a[i].y = read();
s += m - a[i].y * 2;
}
tot = n;
for (int i = 1, x, y; i <= q; ++i) {
x = read();
y = read();
s -= m - a[b[x]].y * 2;
update(1, 1, q, max(c[x], 1), i - 1, b[x]);
c[x] = i;
a[++tot] = node(x, y);
b[x] = tot;
s += m - a[b[x]].y * 2;
ans[i] = s;
}
for (int i = 1; i <= n; ++i) {
update(1, 1, q, max(c[i], 1), q, b[i]);
}
t1.build(1, 1, n);
t2.build(1, 1, n);
dfs(1, 1, q);
fprintf(stderr, "tim: %.5lf\n", tim);
for (int i = 1; i <= q; ++i) {
writeln(ans[i]);
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
flush();
return 0;
}