【JZOJ 4623】搬运干草捆

Description

这里写图片描述

Solution

题解说用splay来解,我不会,我用贪心,
显然的,在使当前答案最优的同时,也要保证当前做到的最后一个干草的高度最高,
把每个修改后高度一样的干草合成一个块,设每个干草原高度为 ai ,修改后的整块的高度为 bk
我们先来考虑局部的解:
当前做到i,
如果 bk=ai ,直接合并上去,
如果 bk>ai ,新开一个块,
如果 bk<ai ,分类讨论:
设块中(含新加的i)的 abk 有x个, a>bk 有y个,
一个结论:在只有两个点的情况下,把一个点升高和把另一个点减低的代价是一样的,
同样的,如果块中 x=y ,那么结论同样成立,因为我们可以把它们看成一高一低两两配对的点对。
x>y 时,只能把i点降低,
x=y 时,代价与上情况相同,因为代价不变,可以升高整块的高度,
找到一个高度 q=minaj>bk{aj} (用主席树)为升高的上限高度,因为如果再升高代价就会变,
如果q大于上一个块的高度 bk1 ,显然不合法,只把当前区间升到 bk1 ,并把这两个块合并,
如果不是,就直接上升到q,

最后的答案就是每个块的代价和,
复杂度: O(nlog(n))
图片from YxuanwKeith
这里写图片描述
这里写图片描述

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
const int N=100500,INF=2070483640;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n,m;
LL ans;
int a[N],za0;
struct wqwq
{int b,x,y,i;LL ans;}za[N];
struct qqww
{int l,r,s;}b[N*50];
int root[N],b0;
struct wwq
{int s,ans;};
void build(int l,int r,int e1,int &e2,int l2)
{
    b[e2=++b0]=b[e1];
    if(l==r){b[e2].s++;return;}
    int t=(l+r)/2;
    if(l2<=t)build(l,t,b[e1].l,b[e2].l,l2);
        else build(t+1,r,b[e1].r,b[e2].r,l2);
    b[e2].s=b[b[e2].l].s+b[b[e2].r].s;
}
wwq find(int l,int r,int e1,int e2,int l1)
{
    wwq ans;ans.s=b[e2].s-b[e1].s;
    if(b[e2].s-b[e1].s==0)return ans;
    if(l==r)
    {
        ans.ans=l;
        return ans;
    }
    int t=(l+r)/2;
    if(t<l1)return find(t+1,r,b[e1].r,b[e2].r,l1);
        else 
        {
            wwq s=find(l,t,b[e1].l,b[e2].l,l1);
            if(s.s)return s;
            return find(t+1,r,b[e1].r,b[e2].r,l1);
        }
}
int main()
{
    int mx=0;
    wwq q;
    read(n);
    fo(i,1,n)mx=max(mx,read(a[i]));
    za[0].b=INF;
    fo(i,1,n)
    {
        build(1,mx,root[i-1],root[i],a[i]);
        if(za[za0].b>a[i])za[++za0].x=1,za[za0].y=0,za[za0].b=a[i],za[za0].ans=0,za[za0].i=i;
        else if(za[za0].b==a[i])za[za0].x++;
        else
        {
            za[za0].y++;za[za0].ans+=a[i]-za[za0].b;
            while(za[za0].x==za[za0].y)
            {
                q=find(1,mx,root[za[za0].i-1],root[i],za[za0].b+1);
                if(q.ans<=za[za0-1].b)za[za0].x+=q.s,za[za0].y-=q.s,za[za0].b=q.ans;
                if(q.ans>=za[za0-1].b)
                {
                    za[za0-1].x+=za[za0].x;
                    za[za0-1].y+=za[za0].y;
                    za[za0-1].ans+=za[za0].ans;
                    za0--;
                }
            }
        }
    }
    ans=0;
    fo(i,1,za0)ans+=za[i].ans;
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值