2018.09.24 bzoj4977: [[Lydsy1708月赛]跳伞求生(贪心+线段树)

传送门
线段树好题。
这题一看我就想贪心。
先把a,b数组排序。
然后我们选择a数组中最大的b个数(不足b个就选a个数),分别贪心出在b数组中可以获得的最大贡献。
这时可以用线段树优化。
然后交上去只能过一个点(雾
调了很久都没有发现错误点。
于是搜题解。
发现大家的做法都跟我不一样233。
但我不能放弃。
就在这时我发现有可能每次贪心出的最大贡献可能是负数233。
于是我们把每次的决策都记下来。
最后枚举删去最小的决策(有可能是负数,这样答案会增加)之后的贡献来更新答案。
代码:

#include<bits/stdc++.h>
#define N 100005
#define ll long long
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
using namespace std;
inline ll read(){
    ll ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int n,m,top;
bool vis[N];
ll ans=0,sum=0,val[N],a[N],s1[N],s2[N];
struct node{int v;ll w;}q[N];
struct Node{int l,r;ll mx;}T[N<<2];
inline bool cmp(node a,node b){return a.v<b.v;}
inline ll max(ll a,ll b){return a>b?a:b;}
inline void pushup(int p){T[p].mx=max(T[lc].mx,T[rc].mx);}
inline void build(int p,int l,int r){
    T[p].l=l,T[p].r=r;
    if(l==r){T[p].mx=q[l].w;return;}
    build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline void update(int p,int k){
    if(T[p].l==T[p].r){T[p].mx=-0x3f3f3f3f;return;}
    if(k<=mid)update(lc,k);
    else update(rc,k);
    pushup(p);
}
inline int ask(int p){
    if(T[p].l==T[p].r)return T[p].l;
    if(T[lc].mx==T[p].mx)return ask(lc);
    return ask(rc);
}
inline int query(int p,int ql,int qr){
    if(ql<=T[p].l&&T[p].r<=qr)return ask(p);
    if(qr<=mid)return query(lc,ql,qr);
    if(ql>mid)return query(rc,ql,qr);
    int L=query(lc,ql,mid),R=query(rc,mid+1,qr);
    return q[L].w>=q[R].w?L:R;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i)a[i]=read();
    sort(a+1,a+n+1);
    for(int i=1;i<=m;++i)q[i].v=read(),q[i].w=read()-q[i].v;
    sort(q+1,q+m+1,cmp),build(1,1,m);
    for(int i=1;i<=m;++i)val[i]=q[i].v;
    int up=max(1,n-m+1);
    for(int i=up;i<=n;++i){
        int pos=lower_bound(val+1,val+m+1,a[i])-val;
        if(a[i]<=val[pos])--pos;
        if(pos<=0)continue;
        ll tmp=query(1,1,pos);
        update(1,tmp),sum+=a[i]+q[tmp].w,s1[++top]=a[i],s2[top]=q[tmp].w,q[tmp].w=-0x3f3f3f3f;
    }
    ans=sum,sort(s1+1,s1+top+1),sort(s2+1,s2+top+1);
    for(int i=1;i<=top;++i)ans=max(ans,(sum-=s1[i]+s2[i]));
    cout<<ans;
    return 0;
}


转载于:https://www.cnblogs.com/ldxcaicai/p/9738216.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值