JOISC 2022 D3T3 蚂蚁与方糖

发布时间 2023-09-08 13:42:16作者: zltzlt

洛谷传送门

LOJ 传送门

UOJ 传送门

神题。

考虑如何计算二分图最大匹配。考虑利用扩展 Hall 定理,即 \(|A| - \max\limits_{S \subseteq A} \{|S| - |N(S)|\}\)。回到原题就是 \(\text{sumred} - \max\limits_{\{[l_i, r_i]\} \text{两两不交}} \sum\limits_{i = 1}^k \text{sumred}(l_i, r_i) - \text{sumblue}(l_i - L, r_i + L)\)

注意到 \(\{[l_i, r_i]\}\) 之间距离一定 \(> 2L\),否则合并一定更优。

考虑用前缀和简化式子。设 \(R_i, B_i\) 分别为红 / 蓝点个数前缀和,设 \(p_i = -R_{i - 1} + B_{i - L - 1}, q_i = R_i - B_{i + L}\),等价于交替选 \(p, q\) 的最大权值和。

容易线段树上维护 \(f_{i, 0/1, 0/1}\) 表示开始选了 \(p / q\),结尾选了 \(p / q\) 的最大权值和。

然后考虑修改:

  • 添加红点,相当于 \(p\)\([x + 1, +\infty)\)\(y\)\(q\)\([x, \infty)\)\(y\)。相当于 \(p, q\)\([x + 1, \infty)\)\(p\)\(y\)\(q\)\(y\)\(q\)\([x, x]\)\(y\)。前者只会影响 \(f_{i, 0, 0}, f_{i, 1, 1}\)

  • 添加蓝点,相当于 \(p\)\([x + L + 1, \infty)\)\(y\)\(q\)\([x - L, \infty)\)\(y\)。对于共同加减的区间可以像上面一样搞。对于 \(q\)\([x - L, x + L + 1)\) 的区间加,因为相邻两个 \(q\) 的距离一定大于 \(2L\),所以这段区间最多只选了一个 \(q\)。对于 \(f_{i, 0/1, 1}\) 加上 \(y\) 即可。

时间复杂度 \(O(q \log q)\)

code
// Problem: P9528 [JOISC 2022 Day3] 蚂蚁与方糖
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P9528
// Memory Limit: 1 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 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<ll, ll> pii;

const int maxn = 500100;

ll n, m, tot, lsh[maxn], f[maxn << 2][2][2], tag1[maxn << 2], tag2[maxn << 2];
struct node {
	ll op, x, y;
} a[maxn];

inline void pushup(int x) {
	for (int i = 0; i < 2; ++i) {
		for (int j = 0; j < 2; ++j) {
			f[x][i][j] = max(f[x << 1][i][j], f[x << 1 | 1][i][j]);
			for (int k = 0; k < 2; ++k) {
				f[x][i][j] = max(f[x][i][j], f[x << 1][i][k] + f[x << 1 | 1][k ^ 1][j]);
			}
		}
	}
}

inline void pushtag1(int x, ll y) {
	f[x][0][0] += y;
	f[x][1][1] -= y;
	tag1[x] += y;
}

inline void pushtag2(int x, ll y) {
	f[x][0][1] += y;
	f[x][1][1] += y;
	tag2[x] += y;
}

inline void pushdown(int x) {
	if (tag1[x]) {
		pushtag1(x << 1, tag1[x]);
		pushtag1(x << 1 | 1, tag1[x]);
		tag1[x] = 0;
	}
	if (tag2[x]) {
		pushtag2(x << 1, tag2[x]);
		pushtag2(x << 1 | 1, tag2[x]);
		tag2[x] = 0;
	}
}

void update1(int rt, int l, int r, int ql, int qr, ll x) {
	if (ql > qr) {
		return;
	}
	if (ql <= l && r <= qr) {
		pushtag1(rt, x);
		return;
	}
	pushdown(rt);
	int mid = (l + r) >> 1;
	if (ql <= mid) {
		update1(rt << 1, l, mid, ql, qr, x);
	}
	if (qr > mid) {
		update1(rt << 1 | 1, mid + 1, r, ql, qr, x);
	}
	pushup(rt);
}

void update2(int rt, int l, int r, int ql, int qr, ll x) {
	if (ql > qr) {
		return;
	}
	if (ql <= l && r <= qr) {
		pushtag2(rt, x);
		return;
	}
	pushdown(rt);
	int mid = (l + r) >> 1;
	if (ql <= mid) {
		update2(rt << 1, l, mid, ql, qr, x);
	}
	if (qr > mid) {
		update2(rt << 1 | 1, mid + 1, r, ql, qr, x);
	}
	pushup(rt);
}

inline int ID(ll x) {
	return lower_bound(lsh + 1, lsh + tot + 1, x) - lsh;
}

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld%lld%lld", &a[i].op, &a[i].x, &a[i].y);
		lsh[++tot] = a[i].x;
	}
	sort(lsh + 1, lsh + tot + 1);
	tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
	ll s = 0;
	for (int i = 1; i <= n; ++i) {
		ll op = a[i].op, x = a[i].x, y = a[i].y;
		if (op == 1) {
			s += y;
			update1(1, 0, tot, ID(x + 1), tot, -y);
			update2(1, 0, tot, ID(x), ID(x), y);
		} else {
			update1(1, 0, tot, ID(x + m + 1), tot, y);
			update2(1, 0, tot, ID(x - m), ID(x + m + 1) - 1, -y);
		}
		printf("%lld\n", s - f[1][0][1]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}