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