格斗

通过题面 , 不难发现 , 答案只和 NUM满级技能个数 和 VAL最小技能等级 有关。所以我们的重点在于改变这两个值。

对于第一个值,首先是要证明一点:

    我们不妨假设,将这些技能按等级从小到大排序后 , 为 a1 a2 a3……an  

    则 当我们决定选取 k 个技能升至满级时 , 必然选择 a,an-1 …… an-k+1 时最优。

    证明如下 : ∵ 有 k 个技能升至满级 

          ∴ ans += cf * k

          ∴ 收益为定值 , 代价越少 ,越优越

          ∴ 当取an , an-1 …… an-k+1 时 ,代价有最小值 = ∑A - a(n-k+1≤i≤n)

          即 取前 k 大的 等级升至满级 , 结果最优 。

这是贪心的前提。

扫描二维码关注公众号,回复: 8923298 查看本文章

还有一点 , 就是对于 技能等级min  的改变 , 需要付出的代价为 NUM最小等级的技能数 (留给读者自行证明)

于是我们就得出了 , 改变答案的两种方法 。

接下来 ,我们开始考虑正解。

我们发现当我们选定将 k 个技能升至满级后 , 此时的想得到最优解 ,就要 最大化 VAL最小技能等级

而对于一组递增的 k ,他们的 VAL最小技能等级 是递减的。

所以 我们考虑枚举 k ,由 0 到 n ,依次求出对于每个 k ,最大的VAL最小技能等级

在做这些之前 , 先将数值排序去重,并记录每种等级的技能有几种,合成一个二元组(pair<long long,int>),由小到大放在一个双端队列(deque)v中。

然后 ,我们求出当 k = 0 时 ,最大的VAL最小技能等级

具体方法是 ,全取出v中的头元素(v.front().first)minn(即技能等级最小值),以及它的个数num(v.front().second)再将这个元素从v中删除

然后再取出现在的头元素 x (pair<long long,int>) 判断是剩余的金币是否能使minn达到 x.first 这样的“高度”。

若能,则将增加代价:(x.first-minn)* num ,然后把 minn赋值为x.first ,最小值个数 num += x.second ,再将这个元素从 v 中转移到 head(数组模拟栈)中。

若不能 则判断还能增大多少,并给minn增大这么多 ,同时加上代价 。

倘若 m 特别大 ,可以将minn'升至最大的技能等级(即将 v 队列的元素取完) ,那么 在判断是否能继续增长 ,方法与上一行类似 ,但要注意不要让minn>A。

代码就像这样:

    while (true)
    {
        if (!v.size())
        {
            LL add=min((m-val_head)/sum_head , A-head[head.size()-1].first);
            minn+=add;
            val_head+=add*sum_head;
            break;
        }
        if (val_head+(v.front().first-minn)*sum_head<=m)
        {
            head.push_back(v.front());
            val_head+=(v.front().first-minn)*sum_head;
            minn=v.front().first;
            sum_head+=v.front().second;
            v.pop_front();
        }
        else
        {
            LL add=(m-val_head)/sum_head;
            minn+=add;
            val_head+=add*sum_head;
            break;
        }
    }

此后,接着增加 k , 依次减小val_head(增大minn的代价)。

每次将当前最大的数字 ,升成 A 。如果 v 队列未空,这个数字是 v队列的右端点 ; 若 v队列为空,则为head 的栈首元素(head[head.size()-1].first)

每次取走一个 , 取走后注意将 v队列 (或head栈)中的数值个数减一 ,若为 数值个数 为0 了,就将其删除。

减小val_head ,就是要方法比较多 , 读者可自行考虑 ,这里我只贴上代码,不作讲解了。(不理解,可以上线问我)

    while (sum_tail<n)
    {
        if (!v.empty())
        {
            tail.push_back(v.back().first);
            val_tail+=A-v.back().first;
            sum_tail++;
            v.back().second--;
            if (!v.back().second) v.pop_back();
        }
        else 
        {
            tail.push_back(head[head.size()-1].first);
            val_tail+=A-head[head.size()-1].first;
            sum_tail++;
            
            
            val_head-=minn-head[head.size()-1].first;
            sum_head--;
            head[head.size()-1].second--;
            if (!head[head.size()-1].second) head.pop_back();
        }
        if (val_tail>m) break;
        while (val_head+val_tail>m)
        {
            LL del=(val_head+val_tail-m)/sum_head;
            if (m<val_head+val_tail-sum_head*del) del++;
            if (minn-del>head[head.size()-1].first) 
            {
                minn-=del;
                val_head-=del*sum_head;
            }
            else 
            {
                val_head-=(minn-head[head.size()-1].first)*sum_head;
                sum_head-=head[head.size()-1].second;
                minn=head[head.size()-1].first;
                v.push_front(head[head.size()-1]);
                head.pop_back();
            }
        }
        ans=max(ans,sum_tail*cf+minn*cm);
    }

下面是完整代码

#include<bits/stdc++.h>
#include<vector>
#include<queue>
#define MAXN 1000005 
#define LL long long
using namespace std;
int n;
//head 表示 增大最小值有关的变量
//tail 表示 将某个技能升为满级有关的变量 
LL A,cf,cm,m,ans,a[MAXN];
deque<pair<LL,int> >v;
vector<pair<LL,int> >head;
vector<LL>tail;
void solve()
{
    LL val_head=0,val_tail=0;//val 表示代价 
    LL sum_head=0,sum_tail=0;//sum 代表个数 sum_head 即为题解中的 num 
    LL minn=0;//同题解中 minn 
    while (true)
    {
        if (!v.size())
        {
            LL add=min((m-val_head)/sum_head , A-head[head.size()-1].first);
            minn+=add;
            val_head+=add*sum_head;
            break;
        }
        if (val_head+(v.front().first-minn)*sum_head<=m)
        {
            head.push_back(v.front());
            val_head+=(v.front().first-minn)*sum_head;
            minn=v.front().first;
            sum_head+=v.front().second;
            v.pop_front();
        }
        else
        {
            LL add=(m-val_head)/sum_head;
            minn+=add;
            val_head+=add*sum_head;
            break;
        }
    }
    ans=max(ans,sum_tail*cf+minn*cm);
    while (sum_tail<n)
    {
        if (!v.empty())
        {
            tail.push_back(v.back().first);
            val_tail+=A-v.back().first;
            sum_tail++;
            v.back().second--;
            if (!v.back().second) v.pop_back();
        }
        else 
        {
            tail.push_back(head[head.size()-1].first);
            val_tail+=A-head[head.size()-1].first;
            sum_tail++;
            
            
            val_head-=minn-head[head.size()-1].first;
            sum_head--;
            head[head.size()-1].second--;
            if (!head[head.size()-1].second) head.pop_back();
        }
        if (val_tail>m) break;
        while (val_head+val_tail>m)
        {
            LL del=(val_head+val_tail-m)/sum_head;
            if (m<val_head+val_tail-sum_head*del) del++;
            if (minn-del>head[head.size()-1].first) 
            {
                minn-=del;
                val_head-=del*sum_head;
            }
            else 
            {
                val_head-=(minn-head[head.size()-1].first)*sum_head;
                sum_head-=head[head.size()-1].second;
                minn=head[head.size()-1].first;
                v.push_front(head[head.size()-1]);
                head.pop_back();
            }
        }
        ans=max(ans,sum_tail*cf+minn*cm);
    }
    return;
}
int main()
{
    scanf("%d %lld %lld %lld %lld",&n,&A,&cf,&cm,&m);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    v.push_back(make_pair(a[1],1));
    for (int i=2;i<=n;i++)
    {
        if (a[i]!=a[i-1]) v.push_back(make_pair(a[i],1));
        else v.back().second++;
    }
    solve();
    printf("%lld\n",ans);
    return 0;
}

还有一点 ,“十年OI一场空,不开long long见祖宗”

猜你喜欢

转载自www.cnblogs.com/maowuyou/p/12240107.html