【NOIP模拟】有趣的有趣的家庭菜园

92 篇文章 0 订阅
48 篇文章 0 订阅

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草中获取的最大利益到底有多少呢?

Solution

O( n3 )

这题就是要找一个上凸的一个序列,这个明显可以用DP来做。
设f[i]为i在这个序列上,前面选进来的数都不比当前这个数大。
那么DP方程很明显f[i]=max(f[j]+b[i]-cost,f[i]),cost为i到j中间所有比h[i]大的数拔掉胡费用和(因为枚举的j为在这个序列中i的上一个数)。
然后再设一个g[i]倒着做一遍,最后的答案为f[i]+g[i]-b[i]的最大值。

O( n2 )

其实DP是可以打成O( n2 )的。
因为必须是h[j]<=h[i]才可以转移,cost随着j越大而越小。
那么我们的j就倒着枚举,然后每当h[j]>h[i]时,把cost加上拔掉j这个草的值,原因很显然。

O(n log n)

首先很明显要做一个离散化。
这个要取一个最大值,明显可以用线段树优化。
因为对于当前这个f[i]的转移,如果要从j转移过来,那么f[j..i]一定要减去中间那些比h[j]和h[i]大的数一定要减去他们的拔草值才能转移。
那么我们每一次把f[i]处理完之后,把比h[i]小的h[k],全部减去i的拔草值,这里用线段树区间减。
然后把i+1到末尾的所有f[l]都对f[i]取一个最大值,这里用线段树做。
注意,线段树最大值和加减的标记下传的过程中要注意,如果先加减之后,然后取了最大值,不能对最大值的标记直接进行加减,再打一个特判的标记就好了。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
typedef long long ll;
using namespace std;
const int maxn=100007;
int i,j,k,l,n,m;
ll tt,ans;
ll a[maxn],b[maxn],c[maxn],d[maxn],tot;
ll f[maxn],g[maxn];
struct nod{
    ll a,d;
}e[maxn];
struct node{
    ll da,biao,biao1,biao2;
}t[maxn*10];
bool cmp(nod x,nod y){
    return x.a<y.a;
}
void doing1(int x,ll y){
    t[x].da=max(t[x].da,y);
    ll u=y-t[x].biao;
    if(t[x].biao2)t[x].biao1=max(t[x].biao1,u);
    else t[x].biao2=1,t[x].biao1=u;
}
void doing2(int x,ll y){
    t[x].da+=y;
    t[x].biao+=y;
}
void doing(int x){
    if(t[x].biao2!=0){
        doing1(x*2,t[x].biao1);
        doing1(x*2+1,t[x].biao1);
        t[x].biao1=t[x].biao2=0;
    }
    if(t[x].biao!=0){
        doing2(x*2,t[x].biao);
        doing2(x*2+1,t[x].biao);
        t[x].biao=0;
    }
}
void change(int x,int l,int r,int y,int z,ll o) {
    if (y>z) return;
    if (l==y&&r==z) {doing2(x,o);return;}
    int mid=(l+r)/2;
    doing(x);
    if (z<=mid) change(x*2,l,mid,y,z,o);
    else if (y>mid) change(x*2+1,mid+1,r,y,z,o);
    else change(x*2,l,mid,y,mid,o),change(x*2+1,mid+1,r,mid+1,z,o);
    t[x].da=max(t[x*2].da,t[x*2+1].da);
}
void change1(int x,int l,int r,int y,int z,ll o) {
    if (y>z) return;
    if (l==y&&r==z) {doing1(x,o);return;}
    int mid=(l+r)/2;
    doing(x);
    if (z<=mid) change1(x*2,l,mid,y,z,o);
    else if (y>mid) change1(x*2+1,mid+1,r,y,z,o);
    else change1(x*2,l,mid,y,mid,o),change1(x*2+1,mid+1,r,mid+1,z,o);
    t[x].da=max(t[x*2].da,t[x*2+1].da);
}
ll find(int x,int l,int r,int y){
    if (l==r) return t[x].da;
    int mid=(l+r)/2;
    doing(x);
    if (y<=mid)return find(x*2,l,mid,y);
    else return find(x*2+1,mid+1,r,y);
}
int main(){
    freopen("herbary.in","r",stdin);
    freopen("herbary.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%lld%lld%lld",&e[i].a,&b[i],&c[i]);
        e[i].d=i;
    } 
    sort(e+1,e+n+1,cmp);
    tot=1;a[e[1].d]=tot;
    fo(i,2,n){
        if(e[i-1].a!=e[i].a)a[e[i].d]=++tot;
        else a[e[i].d]=tot;
    }

    fo(i,1,n){
        f[i]=b[i]+find(1,1,tot,a[i]);
        change(1,1,tot,1,a[i]-1,-c[i]);
        change1(1,1,tot,a[i],tot,f[i]);
    }
    memset(t,0,sizeof(t));
    fod(i,n,1){
        g[i]=b[i]+find(1,1,tot,a[i]);
        change(1,1,tot,1,a[i]-1,-c[i]);
        change1(1,1,tot,a[i],tot,g[i]);
    }
    fo(i,1,n){
        ans=max(ans,f[i]+g[i]-b[i]);
    }
    printf("%lld",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值