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;
}
posted @ 2025-07-02 11:58  徐子洋  阅读(8)  评论(0)    收藏  举报