ccpc2018网络赛 1010

链接

http://acm.hdu.edu.cn/showproblem.php?pid=6447

题意

网格上面有一些村庄 村庄里面有钱

你只能向右、向下、或者向右下走

从a->b,只有当 Xa<Xb && Ya<Yb时候才能获得b点的钱

你一开始在(0,0)

问你最多能获得多少钱

思路

现将y排序 离散化成一段连续的区间

然后将x排序 当成时间 然后按x的大小处理问题

y离散化之后用线段树存储

dp[i]表示到纵坐标i的点能得到的最大值

dp[i]=max(dp[1..i-1])+val

用线段树维护dp[i],优化求最大值的时间

上代码

#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;

const int M=100050;
const int MM=200100;
int n;
int a[M],b[M],val[M],d[M],e[MM],f[MM],dp[MM];

int pxb(int l,int r)
{
    int i=l,j=r,m=b[(l+r)/2],t;
    do
    {
        while (b[i]<m) i++;
        while (b[j]>m) j--;
        if (i<=j)
        {
            t=a[i]; a[i]=a[j]; a[j]=t;
            t=b[i]; b[i]=b[j]; b[j]=t;
            t=val[i]; val[i]=val[j]; val[j]=t;
            i++; j--;
        }
    }
    while (i<=j);
    if (i<r) pxb(i,r);
    if (l<j) pxb(l,j);
}

int pxa(int l,int r)
{
    int i=l,j=r,m=a[(l+r)/2],t;
    do
    {
        while (a[i]<m) i++;
        while (a[j]>m) j--;
        if (i<=j)
        {
            t=a[i]; a[i]=a[j]; a[j]=t;
            t=b[i]; b[i]=b[j]; b[j]=t;
            t=val[i]; val[i]=val[j]; val[j]=t;
            t=d[i]; d[i]=d[j]; d[j]=t;
            i++; j--;
        }
    }
    while (i<=j);
    if (i<r) pxa(i,r);
    if (l<j) pxa(l,j);
}

int weihu(int l,int r,int p,int w,int z)
{
    if (l==r)
    {
        e[l]=max(z,e[l]);
        return e[l];
    }
    int m=(l+r)/2,t;
    if (w<=m)
    {
        t=weihu(l,m,p*2,w,z);
        f[p]=max(t,f[p]);
    } else
    {
        t=weihu(m+1,r,p*2+1,w,z);
        f[p]=max(t,f[p]);
    }
    return f[p];
}

int work(int l,int r,int p,int w)
{
    if (l==r) return e[l];
    if (r<=w) return f[p];
    int m=(l+r)/2,t;
    if (w<=m)
    {
        t=work(l,m,p*2,w);
    } else
    {
        t=max(work(l,m,p*2,w),work(m+1,r,p*2+1,w));
    }
    return t;
}

int main()
{
    int cas;
    scanf("%d",&cas);
    for (int ii=1;ii<=cas;ii++)
    {
        int n;
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&val[i]);
        pxb(1,n);   //  按b排序 为了离散化 将其压成一段连续的区间 


        int k=1;
        b[0]=0;
        for (int i=1;i<=n;i++)
        {
            if (b[i]>b[i-1]) k++;
            d[i]=k;
        }
        pxa(1,n);  //   按横坐标排序 

        a[n+1]=-1;
        for (int i=0;i<=2*(k+1);i++) f[i]=0,e[i]=0,dp[i]=0; 
        int h=1,t;
        while (h<=n)
        {
            t=h;
            while (a[t+1]==a[t]) t++;    //  舍弃掉横坐标相同的 
            for (int i=h;i<=t;i++)
            {
                if (a[i]==0 || b[i]==0) continue;
                dp[i]=work(1,n,1,d[i]-1)+val[i]; 
            }
            for (int i=h;i<=t;i++)
            {
                if (a[i]==0 || b[i]==0) continue;
                weihu(1,n,1,d[i],dp[i]);
            }
            h=t+1;
        }
        printf("%d\n",f[1]);
    }
    return 0;
}
//   这代码是捞毛的,感觉略怪 有空自己写一个

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值