JZOJ 4296【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草中获取的最大利益到底有多少呢?
第i棵草,拔除代价为c[i],收益为b[i]。

Analysis

首先,说明一下,比赛时看这道题的时候看到最大利益条件反射地想到了网络流。然后恶搞构图1个多小时未果(666666)看来还是缺乏经验。
好吧,我应该把题意或目标化简、转化、抽象化。
首先,不管怎样一个序列,总会有一个点是整个序列的最高点。那我们就枚举这个最高点。
f[i] 表示 [1,i] i 为最高点的收益,则

f[i]=b[i]+MAX(f[j]k=j+1,h[k]>h[j]i1c[k])

然后发现会超时。然后发现可以用线段树维护最大值,每当做完i后把前面h小于h[i]的减掉c[i],再加进f[i]。然后发现类似做一遍从右到左的。然后发现 ans=MAX(f[i]+g[i]b[i]) 。然后发现没有然后了。

Code

#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
typedef long long ll;
const int N=100010;
struct node
{
    ll h,p,c;
}a[N];
int n,m,pos[N];
ll f[N],g[N],mx[N*4],add[N*4];
bool cmp(int x,int y){return a[x].hy].h;}
void pre()
{
    scanf("%d",&n);
    fo(i,1,n) scanf("%lld %lld %lld",&a[i].h,&a[i].p,&a[i].c),pos[i]=i;
    sort(pos+1,pos+n+1,cmp);
    ll t=0;
    fo(i,1,n)
        if(a[pos[i]].h!=t)
        {
            t=a[pos[i]].h;
            a[pos[i]].h=++m;
        }
        else a[pos[i]].h=m;
}
void down(int v)
{
    mx[v*2]+=add[v],add[v*2]+=add[v];
    mx[v*2+1]+=add[v],add[v*2+1]+=add[v];
    add[v]=0;
}
ll find(int v,int l,int r,int x,int y)
{
    if(l==x && r==y) return mx[v];
    if(add[v]) down(v);
    int mid=(l+r)>>1;
    if(y<=mid) return find(v*2,l,mid,x,y);
    else
    if(x>mid) return find(v*2+1,mid+1,r,x,y);
    else
    return max(find(v*2,l,mid,x,mid),find(v*2+1,mid+1,r,mid+1,y));
}
void change(int v,int l,int r,int x,int y,ll z)
{
    if(l==x && r==y)
    {
        mx[v]+=z;
        add[v]+=z;
        return;
    }
    if(add[v]) down(v);
    int mid=(l+r)>>1;
    if(y<=mid) change(v*2,l,mid,x,y,z);
    else
    if(x>mid) change(v*2+1,mid+1,r,x,y,z);
    else
    change(v*2,l,mid,x,mid,z),change(v*2+1,mid+1,r,mid+1,y,z);
    mx[v]=max(mx[v*2],mx[v*2+1]);
}
void modify(int v,int l,int r,int x,ll z)
{
    if(l==r)
    {
        mx[v]=z;
        return;
    }
    if(add[v]) down(v);
    int mid=(l+r)>>1;
    if(x<=mid) modify(v*2,l,mid,x,z);
    else modify(v*2+1,mid+1,r,x,z);
    mx[v]=max(mx[v*2],mx[v*2+1]);
}
int main()
{
    freopen("herbary.in","r",stdin);
    freopen("herbary.out","w",stdout);
    pre();
    memset(mx,200,sizeof(mx));
    modify(1,0,m,0,0);
    fo(i,1,n)
    {
        f[i]=a[i].p+find(1,0,m,0,a[i].h);
        change(1,0,m,0,a[i].h,-a[i].c);
        modify(1,0,m,a[i].h,f[i]);
    }
    memset(mx,200,sizeof(mx));
    memset(add,0,sizeof(add));
    modify(1,0,m,0,0);
    fd(i,n,1)
    {
        g[i]=a[i].p+find(1,0,m,0,a[i].h);
        change(1,0,m,0,a[i].h,-a[i].c);
        modify(1,0,m,a[i].h,g[i]);
    }
    ll ans=0;
    fo(i,1,n) ans=max(ans,f[i]+g[i]-a[i].p);
    printf("%lld",ans);
    fclose(stdin);fclose(stdout);
    return 0;
}

你可能感兴趣的:(题解,线段树)