UOJ730 [JOISC 2022] 蚂蚁与方糖
Part1:转化
-
转化为单蚂蚁和单方糖的二分图最大匹配
-
考虑霍尔定理经典推论,\(|V1|-\max_{S\in V1}(|S|-|N(S)|)\),其中 \(N(S)\) 表示与 \(S\) 节点有边的点集。
-
位置 \(x\) 的蚂蚁能匹配的方糖区间为 \([x-L,x+L]\)
-
一个直接的思路是 \(|N(S)|=\sum_{x\in S}|N(x)|\),但是 \(N(x)\) 之间可能有交,导致会算重。
-
为了去除重复贡献点,我们要把有交的区间并到一起,假设将每堆并到一起的蚂蚁记为 \([l_i,r_i]\)。根据定义,显然排序后有 \(l_{i+1}-r_{i}>2L\)
-
记 \(i\) 位置的蚂蚁、方糖数分别为 \(a_i,b_i\),则贡献式为:
\[\sum_{i}(\sum_{j\in[l_i,r_i]}a_j-\sum_{j\in[l_i-L,r_i+L]}b_i) \]
Part2:如何维护
带修且与若干区间贡献和相关的东西,可以考虑用线段树来维护
-
我们将对每个线段树上的区间维护,\(f_{0/1,0/1}\),表示左端点不选/选,右端点不选/选。
-
对蚂蚁的修改是简单的,对应的是线段树上的单点修。
-
对方糖的修改,注意到只会对一些区间产生 \(A\) 的贡献,所以直接做就行了。
具体的,我们按照上述贡献式,把所有和 \([X-L,X+L]\) 有交的线段树区间的 \(\text{DP}\) 值都减去 \(A\)。但是这样在 Pushup 上来的时候会重复减,所以要在重复统计的位置加上 \(A\)。
特别的,我们并不需要在线段树中确保 \([l_i,r_i]\) 两两之间距离 \(>2L\),因为这样必定是不优的。
Part3:代码
点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
const ll INFLL = 1e18;
int Q, L, len, x[N];
array<int, 3> qry[N];
template<typename T>
void ChkMax(T &x, T y) {
if (y > x) x = y;
}
struct SGT {
struct Node {
ll w, lazy, f[2][2];
void tag(ll v) {
w += v, lazy += v;
FL(i, 0, 1) FL(j, 0, 1) {
f[i][j] -= v;
}
ChkMax(f[0][0], 0ll);
}
} t[N << 2];
void PushUp(int p) {
const Node &a = t[p << 1];
const Node &b = t[p << 1 | 1];
FL(i, 0, 1) FL(j, 0, 1) {
t[p].f[i][j] = -INFLL;
}
FL(l1, 0, 1) FL(r1, 0, 1) {
FL(l2, 0, 1) FL(r2, 0, 1) {
ChkMax(t[p].f[l1][r2], a.f[l1][r1] + b.f[l2][r2] + (l2 & r1) * t[p].w);
}
}
}
void PushDown(int p) {
t[p << 1].tag(t[p].lazy);
t[p << 1 | 1].tag(t[p].lazy);
t[p].lazy = 0;
}
void UpdAnt(int p, int l, int r, int x, int v) {
if (l == r) {
t[p].f[1][1] += v;
return;
}
PushDown(p);
int mid = (l + r) >> 1;
if (x <= mid) {
UpdAnt(p << 1, l, mid, x, v);
} else {
UpdAnt(p << 1 | 1, mid + 1, r, x, v);
}
PushUp(p);
}
void UpdSug(int p, int l, int r, int L, int R, int v) {
if (L > R) return;
if (L <= l && r <= R) {
return t[p].tag(v);
}
PushDown(p);
int mid = (l + r) >> 1;
if (R <= mid) {
UpdSug(p << 1, l, mid, L, R, v);
} else if (mid < L) {
UpdSug(p << 1 | 1, mid + 1, r, L, R, v);
} else {
UpdSug(p << 1, l, mid, L, R, v);
UpdSug(p << 1 | 1, mid + 1, r, L, R, v);
t[p].w += v;
}
PushUp(p);
}
ll Query() {
ll ans = 0;
FL(i, 0, 1) FL(j, 0, 1) {
ans = max(ans, t[1].f[i][j]);
}
return ans;
}
} sgt;
int main() {
scanf("%d %d", &Q, &L);
FL(i, 1, Q) {
auto &[typ, X, A] = qry[i];
scanf("%d %d %d", &typ, &X, &A);
if (typ == 1) {
x[++len] = X;
}
}
sort(x + 1, x + len + 1);
len = unique(x + 1, x + len + 1) - x - 1;
ll S = 0;
FL(i, 1, Q) {
auto &[typ, X, A] = qry[i];
if (typ == 1) {
X = lower_bound(x + 1, x + len + 1, X) - x;
sgt.UpdAnt(1, 1, len, X, A);
S += A;
} else {
int l = lower_bound(x + 1, x + len + 1, X - L) - x;
int r = upper_bound(x + 1, x + len + 1, X + L) - x - 1;
sgt.UpdSug(1, 1, len, l, r, A);
}
printf("%lld\n", S - sgt.Query());
}
return 0;
}