【NOIP模拟题】【线段树】【离散化】【DP】2016.11.14第三题 有趣的有趣的家庭菜园 题解

有趣的有趣的家庭菜园(C.c/cpp/pas/in/out)
(Time Limit:1s Memory Limit:256MB)
【Description】
职业经营家庭菜园的JOI君每年在自家的田地中种植一种叫做IOI草的植物。IOI草的种子在冬天被播下,春天会发芽并生长至一个固定的高度。到了秋天,一些IOI草会结出美丽的果实,并被收获,其他的IOI草则会在冬天枯萎。
JOI君的田地沿东西方向被划分为N个区域,从西侧开始的第i个区域中种植着IOI草i。在第i个区域种植的IOI草,在春天的时候高度会生长至Hi,此后便不再生长。如果IOI草i会结出果实,那么将会获得Pi的收益,否则没有收益。
春天到了,查看田地样子的JOI君决定拔掉一些种植的IOI草,使利益最大化。拔掉IOI草i需要Ci的花销,拔掉的IOI草会立刻枯萎。IOI草只能在春天被拔掉,夏天和秋天不能拔掉IOI草。
IOI草是一种非常依靠阳光的植物,如果在夏天某个区域的IOI草的东侧和西侧都有比它高的IOI草存在,那么这株IOI草在秋天便不会结出果实。换句话说,为了让没有被拔掉的IOI草i在秋天结出果实,到了夏天的时候,以下两个条件至少满足一个:
1.对于任意1<=j<=i-1,Hj<=Hi或IOI草j已经被拔除
2.对于任意i+1<=j<=N,Hj<=Hi或IOI草j已经被拔除
用最终收获的果实的总价格减掉拔除IOI草的花销的总和,即为JOI君的收益。那么JOI君能从IOI草中获取的最大利益到底有多少呢?
【Input】
第一行一个正整数N,表示田地被分为了N个区域。
接下来N行,第i行(1<=i<=N)三个空白分割的正整数Hi,Pi,Ci,表示第i株IOI草在春天时高度会生长至Hi,秋天收获的果实的价格为Pi,拔除所需费用为Ci。
【Output】
输出一行一个整数,表示JOI君能获得的最大利益
【Sample Input】
7
22 60 30
46 40 30
36 100 50
11 140 120
38 120 20
24 90 60
53 50 20
【Sample Output】
320
【HINT】
拔除IOI草2和IOI草7,剩余的IOI草如下图所示:
这里写图片描述
IOI草1、3、5、6的果实价格分别为60、100、120、90,拔除IOI草2和IOI草7的花销分别为30、20,总收益为320,这是所有方案中的最大值。
【Data Constraint】
对于30%的数据,N<=20
对于45%的数据,N<=300
对于60%的数据,N<=5000
对于100%的数据:
3<=N<=10^5
1<=Hi<=10^9 (1<=i<=N)
1<=Pi<=10^9 (1<=i<=N)
1<=Ci<=10^9 (1<=i<=N)

爆搜 30分
双向跑DP+线段树、离散化优化 AC
设状态f[i]表示i 这个IOI 草前面没有比它更高的草时,1-i 这些草的纯收益 ;转移为这里写图片描述右面按照同样的方法处理,那么答案就是以某个草及其左面草的收益,和它严格右侧草的收益了;对高度做一个离散化,建一个线段树 ,查询比当前小或等于的最大值。
附代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#define clr(a,x)  memset(x,a,sizeof(x))
#define ll long long
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif

using namespace std;

const int maxn = 100000;
int n,m;
ll ans,t;
int p[maxn+10];
ll f1[maxn+10],f2[maxn+10],num[maxn*4],ad[maxn*4];
struct node
{
    ll h,p,c;
}a[maxn+10];
bool cmp(int x, int y)
{
    return a[x].h < a[y].h;
}

template <class T> inline void read(T &xx)
{
    xx = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        xx = (xx<<1) + (xx<<3) + ch - '0';
        ch = (char)getchar();
    }
    xx *= flag;
}

void init()
{
    read(n);
    for(int i = 1; i <= n; i++)
    {
        read(a[i].h);
        read(a[i].p);
        read(a[i].c);
        p[i]=i;
    }
    sort(p+1 ,p+n+1, cmp);
    for(int i = 1; i <= n; i++)
        if(a[p[i]].h != t)
        {
            t = a[p[i]].h;
            a[p[i]].h = ++m;
        }
        else a[p[i]].h = m;
    clr(200,num);
    return;
}

void pushdown(int i)
{
    num[i*2] += ad[i];
    ad[i*2] += ad[i];
    num[i*2+1] += ad[i];
    ad[i*2+1] += ad[i];
    ad[i] = 0;
    return;
}

ll query(int i, int l, int r, int x, int y)
{
    int mid = (l + r) >> 1;
    if(l == x && r == y) return num[i];
    if(ad[i] != 0) pushdown(i);
    if(y <= mid)
        return query(i*2,l,mid,x,y);
    if(x > mid)
        return query(i*2+1,mid+1,r,x,y);
    if(x <= mid && y > mid)
        return max(query(i*2,l,mid,x,mid), query(i*2+1,mid+1,r,mid+1,y));
}

void updata(int i, int l, int r, int x, int y, ll z)
{
    int mid = (l + r) >> 1;
    if(l == x && r == y)
    {
        num[i] += z;
        ad[i] += z;
        return;
    }
    if(ad[i] != 0) pushdown(i);
    if(y <= mid)
        updata(i*2,l,mid,x,y,z);
    if(x > mid)
        updata(i*2+1,mid+1,r,x,y,z);
    if(x <= mid && y > mid)
    {
        updata(i*2,l,mid,x,mid,z);
        updata(i*2+1,mid+1,r,mid+1,y,z);
    }
    num[i] = max(num[i*2], num[i*2+1]);
    return;
}

void edit(int i, int l, int r, int x, ll y)
{
    int mid = (l + r) >> 1;
    if(l == r)
    {
        num[i] = y;
        return;
    }
    if(ad[i] != 0) pushdown(i);
    if(x <= mid) edit(i*2,l,mid,x,y);
    else edit(i*2+1,mid+1,r,x,y);
    num[i] = max(num[i*2], num[i*2+1]);
    return;
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    init();
    edit(1,0,m,0,0);
    for(int i = 1; i <= n; i++)
    {
        f1[i] = a[i].p + query(1,0,m,0,a[i].h);
        updata(1,0,m,0,a[i].h,-a[i].c);
        edit(1,0,m,a[i].h,f1[i]);
    }
    clr(200,num); clr(0,ad);
    edit(1,0,m,0,0);
    for(int i = n; i >= 1; i--)
    {
        f2[i] = a[i].p + query(1,0,m,0,a[i].h);
        updata(1,0,m,0,a[i].h,-a[i].c);
        edit(1,0,m,a[i].h,f2[i]);
    }
    for(int i = 1; i <= n; i++)
        ans = max(ans, f1[i]+f2[i]-a[i].p);
    printf(AUTO, ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值