模拟赛记录(1):10.24 T3 有趣的有趣的家庭菜园 (线段树优化DP)

有趣的有趣的家庭菜园(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草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)

邻近NOIP,最近基本每天都有模拟赛训练,题不少。其中不乏好题,不乏自己还不会的某类题。比如说这一道。

这道题很容易想到DP,状态和转移也不难设计,设f[i]表示令i为留下的IOI草中,从左数第一高时,1~i这些草带来的收益(果实价格-拔除花销),设g[i]表示令i为留下的IOI草中,从右数第一高时,i~n带来的收益。本质f,g两个数组是在枚举哪棵草作为最高的草。最终答案就是max{f[i]+g[i]-P[i]}。转移对于两个数组是一样的,以f为例:f[i] = max{f[j] - w(j, i) + P[i]},(j < i && h[j] <= h[i]);w(j, i)表示开区间(j, i)中,高度比i高的草的拔除费用之和。

直接枚举是O(n^2),必然要T。我们思考怎么优化。最初我也只是写的n^2的暴力,以为正解是其他做法。看了题解后才想到用线段树来优化转移,这也是第一次写这个。

首先对高度离散化,以高度作为数组下标写一棵线段树,线段树中维护的就是f(以及g)数组。具体过程就是:区间查询,得到当前f[i];用当前i的信息更新线段树;继续查询,得到下一个。这样复杂度就变成了O(nlogn)。对于这道题的具体做法就是,因为i从j转移要满足h[j] <= h[i],所以获取当前f[i]就是query(0, h[i])(为了方便,添加了0号节点)。获取了f[i]后,用i的信息更新线段树,首先要区间修改[0, h[i]-1],全部减去c[i],因为w(j, i)的计算方式使得i在面对其后比它低的IOI草时必须拔除。之后单点修改,把h[i]更新为f[i]。

#include <cstdio>
#include <algorithm>
#include <cstring>
#define mid (l+r>>1)
#define lc (p<<1)
#define rc (p<<1|1)
#define Tree 1,0,n
#define LC lc,l,mid
#define RC rc,mid+1,r
#define M 100005
using namespace std;

long long n, ans, f[M], g[M], H[M], P[M], C[M]; 
long long a, b, c, T[M<<2], tag[M<<2];

void Initialize()
{
    memset(  T, 0, sizeof   T);
    memset(tag, 0, sizeof tag); 
} 

void pushdown(int p)
{
    T[lc] += tag[p], tag[lc] += tag[p];
    T[rc] += tag[p], tag[rc] += tag[p];
    tag[p] = 0;
}

void change(int p, int l, int r)
{
    if(a <= l && r <= b)
        {T[p] += c, tag[p] += c; return ;}

    pushdown(p);
    if(a <= mid) change(LC);
    if(b >  mid) change(RC);
    T[p] = max(T[lc], T[rc]);
}

void change2(int p, int l, int r)
{
    if(l == r) {T[p] = max(T[p], c); return ;}

    pushdown(p);
    if(a <= mid) change2(LC);
    if(a >  mid) change2(RC);
    T[p] = max(T[lc], T[rc]);
}

long long query(int p, int l, int r)
{
    if(a <= l && r <= b) return T[p];
    long long res = -((long long)1<<60);
    pushdown(p);
    if(a <= mid) res = max(res, query(LC));
    if(b >  mid) res = max(res, query(RC));
    return res;
}

void get(long long &x)
{
    char c = getchar(); x = 0;
    while(c < '0' || c > '9') c = getchar();
    while(c <= '9' && c >= '0') x = x*10+c-48, c = getchar();
}

void Read()
{
    get(n);
    for(int i = 1; i <= n; i++)  
        {get(H[i]); get(P[i]); get(C[i]);}
}

void Hash()
{
    pair <int,int> t[100005];
    for(int i = 1; i <= n; i++) 
        t[i].first = H[i], t[i].second = i;

    int num = 0;    
    sort(t+1, t+n+1); 
    t[0].first = -1;
    for(int i = 1; i <= n; i++)
    {
        if(t[i].first != t[i-1].first) num++;
        H[t[i].second] = num;
    }
}

int main()
{
    Read();
    Hash();

    Initialize();
    for(int i = 1; i <= n; i++)
    {
        a = 0, b = H[i]; 
        f[i] = query(Tree)+P[i];
        c = -C[i], b--; change(Tree); 
        a = H[i], c = f[i]; change2(Tree);
    }

    Initialize();
    for(int i = n; i >= 1; i--)
    {
        a = 0, b = H[i]; 
        g[i] = query(Tree)+P[i];
        c = -C[i], b--; change(Tree); 
        a = H[i], c = g[i]; change2(Tree);
    }

    for(int i = 1; i <= n; i++) 
        ans = max(ans, f[i]+g[i]-P[i]);
    printf("%lld\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值