【NOIP2015模拟11.2】有趣的有趣的家庭菜园

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

因为题目要求的是一个类似于单峰函数的东东,我们可以先求两个单调的序列然后拼起来,
有些植物没有价值 但是又没有遮住其他植物,我们可以不管它们,
设一个DP试: fi 表示单调序列的第i个的最大价值,
DP式显然: fi=max(fjik=jcostk(costk<h[i]))
时间 O(n3) ,优化:
搞一颗高度为下标的线段树,记录前面高度从1~i中的最大的价值,
查找直接从1~h[i]找一遍再加上自己的价值,
找完后,我们再把1~(h[i]-1)的所有值减去cost[i],
因为我们以后如要用到1~(h[i]-1)值,一定要把我当前这颗给除掉,
最后更新线段树,
另一个序列一样,倒过来做即可
时间复杂度: O(2nlog(n))

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fo1(i,a,b) for(int i=a;i>=b;i--)
//#define read(a) scanf("%d",&a)
using namespace std;
typedef long long LL;
const int N=100500,maxlongint=2147483640;
const LL INF=9223372036854775800;
int read(int &n)
{
    char ch=getchar();
    while((ch!='-')&&((ch<'0')||(ch>'9')))ch=getchar();
    int q=0,w=1;if(ch=='-')w=-1,ch=getchar();
    while(ch>='0' && ch<='9')q=q*10+ch-48,ch=getchar();n=q*w;return n;
}
LL read(LL &n)
{
    char ch=getchar();
    while((ch!='-')&&((ch<'0')||(ch>'9')))ch=getchar();
    LL q=0,w=1;if(ch=='-') w=-1;else q=q*10+(LL)(ch)-48;ch=getchar();
    while(ch>='0' && ch<='9')q=q*10+(LL)(ch)-48,ch=getchar();n=q*w;return(n);
}
int n,mh;
LL ans;
LL a[N*4],f[N],b[N*4];
LL hi[N],pi[N],co[N];
LL max(LL a,LL b){return a<b?b:a;}
void putd(int l,int r,int e)
{
    a[e]+=b[e];
    if(l!=r)
    {
        b[e*2]+=b[e];b[e*2+1]+=b[e];
    }
    b[e]=0;
}
void doit(int e){a[e]=max(a[e*2],a[e*2+1]);}
void change(int l,int r,int e,int l1,LL l2)
{
    putd(l,r,e);
    if(l==r)
    {
        if(l==l1)a[e]=max(a[e],l2);
        return;
    }
    int t=(l+r)/2;
    if(l1<=t)change(l,t,e*2,l1,l2),putd(t+1,r,e*2+1);
        else putd(l,t,e*2),change(t+1,r,e*2+1,l1,l2);
    doit(e);
}
LL find(int l,int r,int e,int l1,int r1,LL l2,int nol)
{
    putd(l,r,e);
    LL ans=-INF;
    if(l==l1 && r==r1 && l!=nol && r!=nol)
    {
        ans=a[e];
        b[e]+=l2;
        putd(l,r,e);
        return ans;
    }
    if(l==r)return a[e];
    int t=(l+r)/2;
    if(r1<=t)ans=max(ans,find(l,t,e*2,l1,r1,l2,nol)),putd(t+1,r,e*2+1);
        else if(t<l1)putd(l,t,e*2),ans=max(ans,find(t+1,r,e*2+1,l1,r1,l2,nol));
            else 
            {
                ans=max(ans,find(l,t,e*2,l1,t,l2,nol));
                ans=max(ans,find(t+1,r,e*2+1,t+1,r1,l2,nol));
            }
    doit(e);
    return ans;
}
int main()
{
    freopen("herbary.in","r",stdin);
    freopen("herbary.out","w",stdout);
    LL q;
    read(n);
    fo(i,1,n) f[i]=read(hi[i]),read(pi[i]),read(co[i]);
    mh=unique(f+1,f+1+n)-f-1,sort(f+1,f+mh+1);
    fo(i,1,n)hi[i]=upper_bound(f+1,f+mh+1,hi[i])-f-1;
    memset(f,0,sizeof(f)); 
    memset(a,128,sizeof(a));
    change(0,mh,1,0,0);
    fo(i,1,n)
    {
        f[i]=find(0,mh,1,0,hi[i],-co[i],hi[i])+pi[i];
        change(0,mh,1,hi[i],f[i]);
    }
    memset(a,128,sizeof(a));
    change(0,mh,1,0,0);
    ans=-INF;
    fo1(i,n,1)
    {
        q=find(0,mh,1,0,hi[i],-co[i],hi[i]);
        ans=max(ans,q+f[i]);
        change(0,mh,1,hi[i],q+pi[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(dp,线段树,区间修改)