[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章