DP当然是很显然了。
\(f[i][j] = max_{k \le i \wedge l \le j}\{f[k][l]\} + val_{i,j}\).
题目要求横纵坐标只能递增,也就是要求我们在以当前点为右下角的矩形中查一个最大值出来转移,不过并不需要维护一个二维的最大值,我们只需要按纵坐标为第一关键字,横坐标为第二关键字排个序就行了。
这样每次Query()的时候, 出现在\([1, x_i]\)范围内的最大值一定已经是之前做过的合法解.
记得把 \(x\) 离散化一下.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>
using namespace std;
const int maxn = 1e+5 + 5;
class intersec
{
public:
int x, y, v;
intersec(){}
~intersec(){}
bool operator < (const intersec &rhs) const
{
return (y == rhs.y) ? (x < rhs.x) : (y < rhs.y);
}
};
int n, m, k;
int ans, cpy[maxn], tot;
intersec p[maxn];
class SegmentTree
{
public:
SegmentTree() {}
~SegmentTree() {}
void pushup (int cur)
{
mx[cur] = max(mx[cur << 1], mx[cur << 1|1]);
}
void update (int cur, int l, int r, int p, int c)
{
if(l == r) {mx[cur] = c; return;}
int mid = (l + r) >> 1;
if(p <= mid) update(cur << 1, l, mid, p, c);
else update(cur << 1|1, mid + 1, r, p, c);
pushup(cur);
}
int query(int cur, int l, int r, int L, int R)
{
if(L <= l && r <= R) return mx[cur];
int mid = (l + r) >> 1, ret = 0;
if(L <= mid) ret = query(cur << 1, l, mid, L, R);
if(R > mid) ret = max(ret, query(cur << 1|1, mid + 1, r, L, R));
return ret;
}
private:
int mx[maxn << 2];
};
SegmentTree s;
inline int rd()
{
register int x = 0; register char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
return x;
}
int main()
{
n = rd(); m = rd(); k = rd();
for(int i = 1; i <= k; ++i)
{
p[i].x = rd(); p[i].y = rd(); p[i].v = rd();
cpy[i] = p[i].x;
}
cpy[k + 1] = 0;
sort(cpy + 1, cpy + 2 + k);
tot = unique(cpy + 1, cpy + 2 + k) - (cpy + 1);
sort(p + 1, p + 1 + k);
for(int i = 1; i <= k; ++i)
{
p[i].x = lower_bound(cpy + 1, cpy + 1 + tot, p[i].x) - cpy;
int ret = s.query(1, 1, tot, 1, p[i].x);
ret += p[i].v;
s.update(1, 1, tot, p[i].x, ret);
}
printf("%d\n", s.query(1, 1, tot, 1, tot));
return 0;
}