Snowy Smile
Time Limit: 4000/4000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 525 Accepted Submission(s): 165
Problem Description
There are n pirate chests buried in Byteland, labeled by 1,2,…,n. The i-th chest’s location is (xi,yi), and its value is wi, wi can be negative since the pirate can add some poisonous gases into the chest. When you open the i-th pirate chest, you will get wi value.
You want to make money from these pirate chests. You can select a rectangle, the sides of which are all paralleled to the axes, and then all the chests inside it or on its border will be opened. Note that you must open all the chests within that range regardless of their values are positive or negative. But you can choose a rectangle with nothing in it to get a zero sum.
Please write a program to find the best rectangle with maximum total value.
Input
The first line of the input contains an integer T(1≤T≤100), denoting the number of test cases.
In each test case, there is one integer n(1≤n≤2000) in the first line, denoting the number of pirate chests.
For the next n lines, each line contains three integers xi,yi,wi(−109≤xi,yi,wi≤109), denoting each pirate chest.
It is guaranteed that ∑n≤10000.
Output
For each test case, print a single line containing an integer, denoting the maximum total value.
Sample Input
2
4
1 1 50
2 1 50
1 2 50
2 2 -500
2
-1 1 5
-1 1 1
Sample Output
100
6
题目大概意思:
给出平面上 个点,第 个点的坐标是 ,权值为 , ,用一个任意大小的边与坐标轴平行的矩形覆盖点,求矩形能覆盖的点的权值之和的最大值。
分析:
首先对坐标进行离散化处理,则问题转化为了求一个 的矩阵 的所有子矩阵的权值之和的最大值。而由于只有 个点,故 中至多有 个非零元。
对于一般的矩阵求最大子矩阵和,时间复杂度是 或 :
对于一维的最大子段和,我们可以使用尺取法在 的时间复杂度下计算出来,也可以通过使用线段树维护区间最大子段和,在 的时间复杂度下计算出来。
而对于二维的问题,我们可以枚举出矩阵的上下边界,再计算每一列处于上下边界之间的元素的和,将其转化为
个一维问题来解决。
但 的时间复杂度无法在时间限制内解决问题。因此我们考虑如何利用 是稀疏矩阵这一特性降低时间复杂度。
考虑到矩阵中至多有 个非零元,因此每一行只有 个均摊的非零元,因此如果我们已经计算出了以 为上下边界的子矩阵 的每一列的元素之和所组成的向量 ,那么对于以 为上下边界的子矩阵 的每一列的元素之和所组成的向量 , 与 之间在均摊下只有 个元素是不同的。因此如果我们使用了线段树维护 的最大子段和,则可以通过修改 个元素将其转为维护 的最大子段和的线段树,而线段树的每次更新的时间复杂度为 ,因此我们借助维护前一个上下边界内元素的最大子段和的线段树,可以在 的时间复杂度内转变为维护当前上下边界内元素的最大子段和的线段树。那么共有 种边界,在每两种曼哈顿距离为 的边界之间转移的时间复杂度是 ,故算法的总时间复杂度是 .
下面贴代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAX_N = 2005;
struct Star
{
int x;
int y;
int w;
};
Star p[MAX_N];
int kx[MAX_N];
int ky[MAX_N];
ll A[MAX_N][MAX_N];
int G[MAX_N][MAX_N];
int deg[MAX_N];
struct P
{
ll left;
ll right;
ll all;
ll rms;
P() :left(0), right(0), all(0), rms(0) {}
P(const ll& left, const ll& right, const ll& all, const ll& rms)
:left(left), right(right), all(all), rms(rms) {}
};
P dat[(1 << 13) + 5];
int _n;
ll _max(const ll& a, const ll& b);
P unite(P ls, P rs);
void _init(const int n);
void update(const ll& x, int p);
P query(const int& left, const int& right, const int k, const int begin, const int end);
int main()
{
int T, n;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
int kxcnt = 0;
int kycnt = 0;
for (int i = 0; i <= n; i++)
{
memset(A[i], 0, (n + 1) * sizeof(A[0][0]));
}
memset(deg, 0, sizeof(deg));
for (int i = 0; i < n; i++)
{
Star& cur = p[i];
scanf("%d%d%d", &cur.x, &cur.y, &cur.w);
kx[kxcnt++] = cur.x;
ky[kycnt++] = cur.y;
}
sort(kx, kx + kxcnt);
sort(ky, ky + kycnt);
kxcnt = unique(kx, kx + kxcnt) - kx;
kycnt = unique(ky, ky + kycnt) - ky;
for (int i = 0; i < n; i++)
{
Star& cur = p[i];
cur.x = lower_bound(kx, kx + kxcnt, cur.x) - kx;
cur.y = lower_bound(ky, ky + kycnt, cur.y) - ky;
A[cur.x][cur.y] += cur.w;
}
for (int i = 0; i < kxcnt; i++)
{
for (int j = 0; j < kycnt; j++)
{
if (A[i][j])
{
G[i][deg[i]++] = j;
}
}
}
ll ans = 0;
for (int i = 0; i < kxcnt; i++)
{
_init(kycnt);
for (int j = i; j < kxcnt; j++)
{
for (int k = 0; k < deg[j]; k++)
{
update(A[j][G[j][k]], G[j][k]);
}
ans = _max(query(0, n - 1, 0, 0, _n).rms, ans);
}
}
printf("%lld\n", ans);
}
return 0;
}
ll _max(const ll& a, const ll& b)
{
return a > b ? a : b;
}
P unite(P ls, P rs)
{
P res;
res.all = ls.all + rs.all;
res.left = _max(ls.left, ls.all + rs.left);
res.right = _max(rs.right, rs.all + ls.right);
res.rms = _max(_max(ls.rms, rs.rms), ls.right + rs.left);
return res;
}
void _init(const int n)
{
_n = 1;
while (_n < n)
{
_n <<= 1;
}
--_n;
memset(dat, 0, 2 * (_n + 1) * sizeof(dat[0]));
}
void update(const ll& x, int p)
{
p += _n;
P& cur = dat[p];
cur.all += x;
cur.left = cur.right = cur.rms = _max(0ll, cur.all);
while (p)
{
p = (p - 1) >> 1;
dat[p] = unite(dat[(p << 1) + 1], dat[(p << 1) + 2]);
}
}
P query(const int& left, const int& right, const int k, const int begin, const int end)
{
if (left <= begin && end <= right)
{
return dat[k];
}
else if (left <= end && begin <= right)
{
return unite
(
query(left, right, (k << 1) + 1, begin, (begin + end) >> 1),
query(left, right, (k << 1) + 2, ((begin + end) >> 1) + 1, end)
);
}
return P();
}