通过题面 , 不难发现 , 答案只和 NUM满级技能个数 和 VAL最小技能等级 有关。所以我们的重点在于改变这两个值。
对于第一个值,首先是要证明一点:
我们不妨假设,将这些技能按等级从小到大排序后 , 为 a1 a2 a3……an
则 当我们决定选取 k 个技能升至满级时 , 必然选择 an ,an-1 …… an-k+1 时最优。
证明如下 : ∵ 有 k 个技能升至满级
∴ ans += cf * k
∴ 收益为定值 , 代价越少 ,越优越
∴ 当取an , an-1 …… an-k+1 时 ,代价有最小值 = ∑A - ai (n-k+1≤i≤n)
即 取前 k 大的 等级升至满级 , 结果最优 。
这是贪心的前提。
还有一点 , 就是对于 技能等级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见祖宗”