ARC076D Yakiniku Restaurants

发布时间 2023-12-07 21:37:49作者: cxqghzj

题意

\(n\) 个商店。每个商店有 \(m\) 个物品。每个物品的价值为 \(b_{i, j}\)。每种物品只能被购买一次。

你可以选择一个起点,在任意商店结束购买。获得的价值为 \(m\) 个物品之和减去路程。

求最大可获得的价值。

\(n \le 5e3, m \le 200\)

Sol

不难发现,最优的方案一定是一个两端必选的区间。

当前的最优方案固然为每种物品在区间内的最大值之和。

然后我们发现,我们完全没法快速的维护这个东西。

不妨换一种思路,考虑每个物品会在哪里有贡献。

不难想到,设 \(i\) 号物品的前一个比她大的物品为 \(l\),后一个比她大的物品为 \(r\)

注意到当且仅当左端点在 \([l, i]\),右端点在 \([i, r]\) 时,她才会有贡献。

显然这个可以用二维差分维护。

那么这道题就做完了,时间复杂度:\(O(nm + n ^ 2)\)

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <stack>
#define int long long
#define pii pair <int, int>
using namespace std;
#ifdef ONLINE_JUDGE

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;

#endif
int read() {
	int p = 0, flg = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') flg = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		p = p * 10 + c - '0';
		c = getchar();
	}
	return p * flg;
}
void write(int x) {
	if (x < 0) {
		x = -x;
		putchar('-');
	}
	if (x > 9) {
		write(x / 10);
	}
	putchar(x % 10 + '0');
}
#define fi first
#define se second

const int N = 5e3 + 5, M = 205;

array <array <int, N>, M> mp;
array <int, N> s;


array <pii, N> isl;
array <array <int, N>, N> cur;

void add(int x_1, int y_1, int x_2, int y_2, int z) {
	cur[x_1][y_1] += z, cur[x_2 + 1][y_2 + 1] += z;
	cur[x_2 + 1][y_1] -= z, cur[x_1][y_2 + 1] -= z;
}

signed main() {
	int n = read(), m = read();
	for (int i = 2; i <= n; i++)
		s[i] = read();
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			mp[j][i] = read();
	for (int i = 1; i <= m; i++) {
		stack <int> stk;
		for (int j = 1; j <= n; j++) {
			while (!stk.empty() && mp[i][stk.top()] < mp[i][j]) stk.pop();
			if (!stk.empty())
				isl[j].fi = stk.top() + 1;
			else
				isl[j].fi = 1;
			stk.push(j);
		}
		while (!stk.empty())
			stk.pop();
		for (int j = n; j >= 1; j--) {
			while (!stk.empty() && mp[i][stk.top()] <= mp[i][j]) stk.pop();
			if (!stk.empty())
				isl[j].se = stk.top() - 1;
			else
				isl[j].se = n;
			stk.push(j);
		}
		for (int j = 1; j <= n; j++)
			add(isl[j].fi, j, j, isl[j].se, mp[i][j]);
			/* write(isl[j].fi), putchar(32); */
			/* write(isl[j].se), puts(""); */
		/* } */
		/* puts(""); */
	}
	/* for (int i = 1; i <= n; i++) cur[i] += cur[i - 1]; */
	/* for (int i = 1; i <= n; i++) { */
		/* for (int j = 1; j <= n; j++) */
			/* write(cur[i][j]), putchar(32); */
		/* puts(""); */
	/* } */
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		int tp = 0;
		for (int j = 1; j <= n; j++) {
			cur[i][j] += cur[i][j - 1] + cur[i - 1][j] - cur[i - 1][j - 1];
			if (i > j) continue;
			ans = max(ans, cur[i][j] - tp);
			tp += s[j + 1];
		}
	}
	/* for (int i = 1; i <= n; i++) { */
		/* for (int j = 1; j <= n; j++) */
			/* write(cur[i][j]), putchar(32); */
		/* puts(""); */
	/* } */
	write(ans), puts("");

	return 0;
}