[bzoj4977]跳伞求生

题目大意

有n个队友和m个敌人,每个队友有一个攻击力ai,每个敌人有攻击力bi和价值ci。你可以选择若干个队友,每个队友i去怼一个敌人j(i,j两两不同),当ai>bj时,你的队友可以对答案造成ai-bj+cj的贡献。问答案最大可以是多少。

n,m≤100000

分析

我首先往贪心方面想。
考虑把队友按a升序排序,敌人按b升序排序。然后枚举攻击力,开一个优先队列维护可以怼的敌人的c-b。然后对于一个对友,如果在队列里有人可以怼,那么把这个人删去并加上相应的答案,否则就换掉一个a最小的队友(显然可以)。
但是这样做会错,因为你求的是在选择的队友最多情况下的答案。有敌人c-b为负数时会错,这时应该舍弃掉一些敌人。
按照上面的方法可以跑出一个解,之后要做的是:把用到的队友和怼掉的敌人存下来,然后每次删掉a最小的队友和c-b最小的敌人,得到另一个解(这样删仍然合法,因为队友从小到大删不会产生不合法配对),并更新答案。
时间复杂度 O((n+m)log(n+m))

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N=2e5+5;

typedef long long LL;

int n,m,h,t,D[N];

LL ans,s;

priority_queue <int> f;

priority_queue <int,vector<int>,greater<int> > g;

struct Data
{
    int x,y,typ;
}A[N];

bool operator < (const Data &a,const Data &b)
{
    return a.x<b.x || a.x==b.x && a.typ<b.typ;
}

char c;

int read()
{
    int x=0,sig=1;
    for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
    for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
    return x*sig;
}

int main()
{
    n=read(); m=read();
    for (int i=0;i<n;i++)
    {
        A[i].x=read(); A[i].typ=0;
    }
    for (int i=0;i<m;i++)
    {
        A[n].x=read(); A[n].y=read()-A[n].x; A[n++].typ=1;
    }
    sort(A,A+n);
    h=1;
    for (int i=0;i<n;i++)
    {
        if (A[i].typ==1) f.push(A[i].y);else
        {
            if (f.empty())
            {
                if (h<=t)
                {
                    s+=A[i].x-D[h++];
                    D[++t]=A[i].x;
                }
            }else
            {
                s+=A[i].x+f.top();
                g.push(f.top());
                f.pop();
                D[++t]=A[i].x;
            }
        }
    }
    ans=s;
    for (int i=h;i<=t;i++)
    {
        s-=g.top()+D[i];
        ans=max(ans,s);
        g.pop();
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值