[题解] [JSOI2010] 挖宝藏

题面

题解

我们发现挖掘一个点所需要的方块是一个向上的倒三角, 我们设这个倒三角在 \(y = -1\) 上的左右端点分别为 \(l, r\)
不难发现, 一个 \([l, r]\) 区间仅对应一个点, 也就是说, 每个点所需要的倒三角都是不同的
如果一个区间被另外一个区间完全覆盖, 那么选了大的那个区间, 小区间的贡献就可以直接加上了, 而不需要算选小区间的代价
同理, 若两个区间相交, 那么选这两个区间的利润就是他们的贡献减去他们的代价再加上重复部分的代价
因为这一部分被多减了一次
但是从这里又会发现一个问题, 若两个区间同时完全覆盖一个小区间, 并且这个两个区间重叠的区间也完全覆盖这个小区间
那这个小区间的贡献又会被多算一次, 要减掉
我们把区间按照右端点排序, 并预处理出每个区间完全覆盖的区间, 将贡献加上去
设 \(f[i]\) 代表前 \(i\) 个区间, \(i\) 必选的最大利润
考虑枚举 \(j\)
若 \(j\) 区间被 \(i\) 区间完全覆盖, 贡献已经算进 \(i\) 区间了, 不从这种 \(j\) 转移
若 \(j\) 区间与 \(i\) 区间有交, 减去交的这一部分的重复贡献, 加上交的这一部分的重复代价
若 \(j\) 区间与 \(i\) 区间无交, 直接加上 \(j\) 的利润即可
这样子是 \(O(n^3)\) 的
考虑优化算重复贡献的部分, 发现右端点单调不降, 拿个指针维护一下即可

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

int n, f[1005], ans, cnt, r; 
struct node
{
	int l, r, v1, v2, c;
	bool operator < (const node &p) const
		{
			return r == p.r ? l < p.l : r < p.r; 
		}
} p[1005]; 

template < typename T >
inline T read()
{
	T x = 0, w = 1; char c = getchar();
	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return x * w; 
}

int calc(int r, int l)
{
	int res = (r + l) & 1;
	return (r - l + 2 + res) * (r - l + 2 - res) / 4; 
}

int main()
{
	n = read <int> ();
	for(int x, y, i = 1; i <= n; i++)
	{
		x = read <int> (), y = read <int> ();
		p[i].l = x + y + 1, p[i].r = x - y - 1;
		p[i].v1 = read <int> (), p[i].c = calc(p[i].r, p[i].l); 
	}
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			if(p[j].l >= p[i].l && p[j].r <= p[i].r)
				p[i].v2 += p[j].v1;
	sort(p + 1, p + n + 1); 
	f[0] = 0;
	for(int i = 1; i <= n; i++)
	{
		f[i] = p[i].v2 - p[i].c, r = 1, cnt = 0; 
		for(int tmp, j = 1; j < i; j++)
		{
			if(p[j].r >= p[i].l && p[j].l < p[i].l)
			{
				while(r <= i && p[r].r <= p[j].r)
				{
					if(p[r].l >= p[i].l)
						cnt += p[r].v1;
					r++; 
				}
				tmp = calc(p[j].r, p[i].l); 
				f[i] = max(f[i], f[j] + p[i].v2 - p[i].c + tmp - cnt); 
			}
			if(p[j].r < p[i].l)
				f[i] = max(f[i], f[j] + p[i].v2 - p[i].c); 
		}
	}
	for(int i = 0; i <= n; i++)
		ans = max(ans, f[i]);
	printf("%d\n", ans); 
	return 0; 
}
/*
5
1 -1 2
0 -1 2
4 -1 1
3 -1 2
2 -1 2
 */
posted @ 2020-02-04 10:55  ztlztl  阅读(269)  评论(0)    收藏  举报