平面上有 k k 个点,每一个点有一个权值,求一条路径使得从左上角到右下角最短同时经过的点得权值总和最大。
实际上不难发现这就是一个DP,每一个点可以从横坐标比它大并且纵坐标比它大的点转移过来,所以我们必须要保证它之前的状态在它的前面处理,其实这就是一个经典的二维偏序,我们先按照横坐标从大到小排序,再按照纵坐标从大到小排序之后处理就可以保证状态转移的合法性了。用一个线段树来维护最大值即可,相当于把后面的行都压缩在了一个线段树里面,反正是查询这里面所有的最大值。
/*=============================
* Author : ylsoi
* Problem : travel
* Algorithm : Segment_Tree
* Time : 2018.7.23
* ===========================*/
#include
#define REP(i,a,b) for(int i=a;i<=b;++i)
#define DREP(i,a,b) for(int i=a;i>=b;--i)
typedef long long ll;
using namespace std;
void File(){
freopen("20180722T2.in","r",stdin);
freopen("20180722T2.out","w",stdout);
}
const int maxn=1e5+10;
int n,m,k,toty;
struct node{
int x,y,w;
bool operator < (const node & tt) const {
if(x!=tt.x)return xelse return ymap<int,int>mp;
map<int,int>::iterator it;
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc rt<<1
#define rc rt<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
int Max[maxn<<2];
void update(int rt,int l,int r,int pos,int x){
if(l==r)chkmax(Max[rt],x);
else{
if(pos<=mid)update(lson,pos,x);
else update(rson,pos,x);
Max[rt]=max(Max[lc],Max[rc]);
}
}
int query(int rt,int l,int r,int L,int R){
if(L<=l && r<=R)return Max[rt];
int ret=0;
if(L<=mid)chkmax(ret,query(lson,L,R));
if(R>=mid+1)chkmax(ret,query(rson,L,R));
return ret;
}
}T;
int main(){
File();
scanf("%d%d%d",&n,&m,&k);
REP(i,1,k){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
mp.insert((pair<int,int>){a[i].y,0});
}
for(it=mp.begin(),toty=1;it!=mp.end();++it,++toty)
mp[it->first]=toty;
REP(i,1,k)a[i].y=mp[a[i].y];
sort(a+1,a+k+1);
DREP(i,k,1){
int val=T.query(1,1,toty,a[i].y,toty);
T.update(1,1,toty,a[i].y,val+a[i].w);
}
printf("%d\n",T.query(1,1,toty,1,toty));
return 0;
}