题目链接:hdu-6447.
题目描述
          
\;\;\;\;\;
从
(
0
,
0
)
(0, 0)
(0,0)走到
(
1
0
9
,
1
0
9
)
(10^{9},10^{9})
(109,109),可以向三个方向走,即
(
x
,
y
)
(x,y)
(x,y)可以到
(
x
+
1
,
y
)
(x+1,y)
(x+1,y)、
(
x
,
y
+
1
)
(x, y+1)
(x,y+1)、
(
x
+
1
,
y
+
1
)
(x+1, y+1)
(x+1,y+1),现在其中
n
n
n个点有价值,当以
(
x
−
1
,
y
−
1
)
(x-1,y-1)
(x−1,y−1)
→
\rightarrow
→
(
x
,
y
)
(x,y)
(x,y)方式走到点
(
x
,
y
)
(x,y)
(x,y)时,可以得到该点的价值。
          
\;\;\;\;\;
求:从
(
0
,
0
)
(0, 0)
(0,0)走到
(
1
0
9
,
1
0
9
)
(10^{9},10^{9})
(109,109),可以获得的最大价值。
          
\;\;\;\;\;
1
≤
n
≤
1
0
5
1\leq n\leq 10^{5}
1≤n≤105,
0
≤
x
,
y
≤
1
0
9
0\leq x,y\leq 10^{9}
0≤x,y≤109,点的价值
0
≤
v
≤
1
0
3
0\leq v\leq 10^{3}
0≤v≤103.
思路
首先可以考虑哪些点有更新关系。 如下图:
          
\;\;\;\;\;
对于点
(
x
,
y
)
(x,y)
(x,y),能更新它的点只有区域
2
2
2的点,它能更新的点只有区域
4
4
4的点。即
x
k
<
x
,
y
k
<
y
x_{k}<x,y_{k}<y
xk<x,yk<y 的点,可能更新
(
x
,
y
)
(x,y)
(x,y). (思路停滞处)
          
\;\;\;\;\;
更新条件:
x
x
x 坐标 和
y
y
y 坐标均小于它的所有点都可能将它更新。
将所有点按照
x
x
x 从小到大,
y
y
y 从大到小排序。
f
[
i
]
f[i]
f[i] 表示从
(
0
,
0
)
走
到
(0,0)走到
(0,0)走到纵坐标为
[
1...
i
]
[1...i]
[1...i]的点的最大价值。如果当前点是
(
x
,
y
,
v
)
(x,y,v)
(x,y,v),那么当前点的最大价值为
f
[
y
−
1
]
+
v
f[y-1] + v
f[y−1]+v .
f
f
f 是持续更新的,当前所用
f
f
f的是由之前的点更新的,所以一定满足更新条件。
          
\;\;\;\;\;
用线段树维护
f
f
f. 但是坐标范围为
[
0
,
1
0
9
]
[0,10^{9}]
[0,109],所以首先根据坐标值
y
y
y 进行离散化处理,即可用线段树维护
f
f
f.
          
\;\;\;\;\;
设离散化后某个点的纵坐标为
y
y
y,则查询区间
[
1
,
y
−
1
)
]
[1,y-1)]
[1,y−1)]的最大值
M
a
x
Max
Max,
M
a
x
+
v
Max + v
Max+v 即为当前的最大价值,再用此最大价值更新区间
[
1
,
y
]
[1,y]
[1,y]。(这里并不是简单的前缀)
注意点:
          
\;\;\;\;\;
纵坐标是按照从大到小排列的,这样才能保证更新正确。比如现在有点
P
1
(
1
,
1
,
v
1
)
,
P
2
(
1
,
2
,
v
2
)
,
P
3
(
1
,
3
,
v
3
)
P_{1}(1,1,v_{1}),P_{2}(1,2,v_{2}),P_{3}(1,3,v_{3})
P1(1,1,v1),P2(1,2,v2),P3(1,3,v3),如果是按照纵坐标从小到大排序,更新点
P
3
P_{3}
P3时,会用到
P
1
,
P
2
P_{1},P_{2}
P1,P2的值,而实际上这样更新是不对的。按照纵坐标从大到小排序就不会这样。(有点类似于01背包?)
代码
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define lson(u) (u<<1)
#define rson(u) (u<<1|1)
const int MAXN = 100005;
struct node {
int l, r, Max;
}t[MAXN << 4];
struct point {
int x, y, v;
bool operator< (const point &P) const {
return x == P.x ? y > P.y : x < P.x;
}
}Point[MAXN << 4];
int lst_y[MAXN];
void push_up(int u) {
t[u].Max = max(t[lson(u)].Max, t[rson(u)].Max);
return;
}
void build(int l, int r, int u) {
t[u].l = l;
t[u].r = r;
if(l == r) {
t[u].Max = 0;
return;
}
int mid = l + r >> 1;
build(l, mid, lson(u));
build(mid + 1, r, rson(u));
push_up(u);
return;
}
int Query_Max(int L, int R, int u) {
if(L <= t[u].l && R >= t[u].r) {
return t[u].Max;
}
int mid = t[u].l + t[u].r >> 1, Max = 0;
if(mid >= L) Max = Query_Max(L, R, lson(u));
if(mid < R) Max = max(Max, Query_Max(L, R, rson(u)));
return Max;
}
void update(int place, int x, int u) {
if(t[u].l == t[u].r && t[u].l == place) {
t[u].Max = max(t[u].Max, x);
return;
}
int mid = t[u].l + t[u].r >> 1;
if(mid >= place) update(place, x, lson(u));
if(mid < place) update(place, x, rson(u));
push_up(u);
return;
}
int main() {
int T, n;
scanf("%d", &T);
while(T--) {
int cnt = 1;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d%d%d", &Point[i].x, &Point[i].y, &Point[i].v);
lst_y[cnt++] = Point[i].y;
}
sort(lst_y + 1, lst_y + cnt);
int m = 0;
m = unique(lst_y + 1, lst_y + cnt) - lst_y; // 去重
for(int i = 1; i <= n; i++) {
Point[i].y = lower_bound(lst_y + 1, lst_y + m, Point[i].y) - lst_y; // 离散化
}
build(1, m, 1);
sort(Point + 1, Point + n + 1);
int ans = 0;
for(int i = 1; i <= n; i++) {
int y = Point[i].y;
int tmp = Query_Max(1, y - 1, 1) + Point[i].v; // f[y-1] + v
ans = max(ans, tmp);
update(y, tmp, 1); // 更新
}
printf("%d\n", ans);
}
return 0;
}
/*
1
3
1 1 1
1 2 2
3 3 1
4
1 1 1
2 2 1
2 3 5
3 3 1
*/