洛谷 P8490 [IOI2022] 鲶鱼塘

发布时间 2023-07-22 19:43:43作者: zltzlt

洛谷传送门

LOJ 传送门

不算很难的题,但是调起来比较恶心。

下文默认下标从 \(1\) 开始。

设第 \(i\) 列长堤的高度为 \(h_i\)。考虑观察一些性质。

Observation 1:若 \(h_{i - 1} < h_i > h_{i + 1}\),那么 \(h_i = n\) 一定不劣。

\(h_i < n\)\([h_i + 1, n]\) 的鱼是抓不到的,并且增大 \(h_i\) 还有机会让旁边两列抓到更多的鱼,所以还不如 \(h_i = n\)

Observation 2:若 \(h_{i - 1} > h_i < h_{i + 1}\),那么 \(h_i = 0\) 一定不劣。

\(h_i > 0\)\([1, h_i]\) 的鱼是抓不到的,让 \(h_i = 0\) 就能抓到 \([1, h_i]\) 的鱼。

Observation 3:对于一组连续的长堤,其一定是单峰的,并且其中 \(\max h_i = n, \min h_i = 0\)

由上面两个观察易得。

Observation 4:\((i, h_i + 1)\) 一定有鱼。

如果 \((i, h_i + 1)\) 没有鱼,那我们能够增大 \(h_i\) 直到 \((i, h_i + 1)\) 有鱼,这样不会使第 \(i\) 列抓到更少的鱼,而且还有机会使旁边两列抓到更多的鱼。

然后我们就可以开始设计 dp 了。设 \(f_{i, j, 0/1}\)\(h_i = j\)\(h_i\) 处在上升还是下降阶段,能抓到的鱼的价值最大和。这里我们强制规定 \(h_i = n\)\(h_i\) 处于下降阶段,\(h_i = 0\)\(h_i\) 处于上升阶段。

转移大概就是,\(f_{i, j, 0}\)\(\forall k \in [0, j], f_{i - 1, k, 0}\) 转移过来,差分一下加上第 \(i - 1\) 列中行坐标 \(\in [k + 1, j]\) 的鱼的价值和;\(f_{i, j, 1}\)\(\forall k \in [j, n], f_{i - 1, k, 1}\) 转移过来,仍然差分一下加上第 \(i\) 列中行坐标 \(\in [j + 1, k]\) 的鱼的价值和。特别地,\(f_{i, 0, 0}\) 可以从任意 \(f_{i - 1, j, 1}\) 转移过来,\(f_{i, n, 1}\) 可以从任意 \(f_{i - 1, j, 0}\) 转移过来,加上第 \(i - 1\) 列行坐标 \(\in [j + 1, n]\) 的鱼的价值和。

对于价值和的计算,为了方便,我们强制规定,当 \(h_i\) 处于上升阶段时,\(f_i\) 计算 \([1, i - 1]\) 的价值和;\(h_i\) 处于下降阶段时,\(f_i\) 计算 \([1, i]\) 的价值和。但是这样有个问题,就是如果 \(h_{i - 1} > h_i = 0 < h_{i + 1}\)\(h_{i - 1} > h_{i + 1}\),我们的规定会导致第 \(i\) 列行坐标 \(\in [h_{i + 1} + 1, h_{i - 1}]\) 的鱼漏算进答案里面。这种情况我们直接从 \(f_{i - 2}\) 转移过来即可。

本质不同状态数只有 \(O(n + m)\) 种,因此使用 unordered_map 存储状态即可。

使用树状数组分别计算后缀和、前缀 \(\max\)、后缀 \(\max\)(记得每次转移完要清空),时间复杂度 \(O((n + m) \log n)\)

code
// Problem: P8490 [IOI2022] 鲶鱼塘
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8490
// Memory Limit: 2 MB
// Time Limit: 500000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 300100;
const ll inf = 0x3f3f3f3f3f3f3f3fLL;

ll n, m;
unordered_map<ll, ll> f[maxn][2];
vector<pii> vc[maxn];

struct BIT {
	ll c[maxn];
	
	inline void update(ll x, ll d) {
		for (int i = x; i; i -= (i & (-i))) {
			c[i] += d;
		}
	}
	
	inline ll query(int x) {
		ll res = 0;
		for (int i = x; i <= n; i += (i & (-i))) {
			res += c[i];
		}
		return res;
	}
	
	inline void clear(int x) {
		for (int i = x; i; i -= (i & (-i))) {
			c[i] = 0;
		}
	}
} t1;

struct mxBIT {
	ll c[maxn], d[maxn];
	
	inline void init() {
		for (int i = 0; i < maxn; ++i) {
			c[i] = d[i] = -inf;
		}
	}
	
	inline void clear(int x) {
		for (int i = (++x); i <= n + 1; i += (i & (-i))) {
			c[i] = -inf;
		}
		for (int i = x; i; i -= (i & (-i))) {
			d[i] = -inf;
		}
	}
	
	inline void update(int x, ll y) {
		for (int i = (++x); i <= n + 1; i += (i & (-i))) {
			c[i] = max(c[i], y);
		}
		for (int i = x; i; i -= (i & (-i))) {
			d[i] = max(d[i], y);
		}
	}
	
	inline ll query1(int x) {
		ll res = -inf;
		for (int i = (++x); i; i -= (i & (-i))) {
			res = max(res, c[i]);
		}
		return res;
	}
	
	inline ll query2(int x) {
		ll res = -inf;
		for (int i = (++x); i <= n + 1; i += (i & (-i))) {
			res = max(res, d[i]);
		}
		return res;
	}
} t2;

ll max_weights(int _n, int _m, vector<int> _x, vector<int> _y, vector<int> _w) {
	n = _n;
	m = _m;
	for (int i = 0; i < m; ++i) {
		int x = _x[i] + 1, y = _y[i] + 1, w = _w[i];
		vc[x].pb(y, w);
	}
	f[0][0][0] = 0;
	t2.init();
	ll ans = 0;
	for (int i = 1; i <= n; ++i) {
		for (pii p : vc[i - 1]) {
			t1.update(p.fst, p.scd);
		}
		vector<int> S;
		for (pii p : f[i - 1][0]) {
			if (p.scd < 0) {
				continue;
			}
			S.pb(p.fst);
			t2.update(p.fst, p.scd + t1.query(p.fst + 1));
		}
		for (pii p : vc[i]) {
			if (p.fst == 1) {
				continue;
			}
			int x = p.fst - 1;
			f[i][0][x] = t2.query1(x) - t1.query(x + 1);
		}
		f[i][0][0] = t2.query1(0) - t1.query(1);
		f[i][1][n] = t2.query1(n);
		for (pii p : vc[i - 1]) {
			t1.clear(p.fst);
		}
		for (int x : S) {
			t2.clear(x);
		}
		vector<int>().swap(S);
		if (i > 2) {
			for (pii p : vc[i - 1]) {
				t1.update(p.fst, p.scd);
			}
			for (pii p : f[i - 2][1]) {
				if (p.scd < 0) {
					continue;
				}
				S.pb(p.fst);
				t2.update(p.fst, p.scd + t1.query(1) - t1.query(p.fst + 1));
			}
			for (pii p : vc[i]) {
				if (p.fst == 1) {
					continue;
				}
				int x = p.fst - 1;
				f[i][0][x] = max(f[i][0][x], t2.query2(x));
			}
			f[i][0][0] = max(f[i][0][0], t2.query1(n));
			for (pii p : vc[i - 1]) {
				t1.clear(p.fst);
			}
			for (int x : S) {
				t2.clear(x);
			}
			vector<int>().swap(S);
		}
		for (pii p : vc[i]) {
			t1.update(p.fst, p.scd);
		}
		ll mx = 0;
		for (pii p : f[i - 1][1]) {
			if (p.scd < 0) {
				continue;
			}
			mx = max(mx, p.scd);
			t2.update(p.fst, p.scd - t1.query(p.fst + 1));
			S.pb(p.fst);
		}
		for (pii p : vc[i]) {
			if (p.fst == 1) {
				continue;
			}
			int x = p.fst - 1;
			f[i][1][x] = t2.query2(x) + t1.query(x + 1);
		}
		if (i == n) {
			ans = max(ans, t2.query1(n) + t1.query(1));
		}
		f[i][0][0] = max(f[i][0][0], mx);
		f[i][1][n] = max(f[i][1][n], t2.query2(n));
		for (pii p : vc[i]) {
			t1.clear(p.fst);
		}
		for (int x : S) {
			t2.clear(x);
		}
	}
	for (int i = 0; i <= 1; ++i) {
		for (pii p : f[n][i]) {
			ans = max(ans, p.scd);
		}
	}
	return ans;
}

// int main() {
	// int n, m;
	// vector<int> a, b, c;
	// scanf("%d%d", &n, &m);
	// for (int i = 0, x, y, z; i < m; ++i) {
		// scanf("%d%d%d", &x, &y, &z);
		// a.pb(x);
		// b.pb(y);
		// c.pb(z);
	// }
	// printf("%lld\n", max_weights(n, m, a, b, c));
	// return 0;
// }