洛谷 P6155 修改 题解
题目链接
题目大意
给定两序列 \(a_{[1..n]}\) 与 \(b_{[1..n]}\),其中 \(b_{[1..n]}\) 可以随意调整顺序。若将 \(a_i\) 加 \(1\) 的代价为 \(b_i\),求使 \(a_i\) 互不相同的最小代价为多少。
思路分析
对于最小花费,且代价可自由排序,容易想到按需要加 \(1\) 次数分配代价。具体的,应让加 \(1\) 次数越多的元素代价越小。另外,对于 \(a_i\) 和 \(a_j\),当修改 \(a_i\) 的某一时刻 \(a_i=a_j\) 时,由前面的贪心可知 \(b_i\le b_j\),此时显然让 \(a_i\) 再增加花费更低。
由前面的分析,原来 \(a_i<a_j\) 的两元素操作完后 \(a_i>a_j\)。所以我们可以把 \(a\) 数组存进桶里,再倒序遍历,每次让当前元素加到最近的符合题意的位置。显然这些位置是从 int 范围中扣掉 \(a_{[1..n]}\) 得到,为几块连续的数。我们可以用栈来存储每一块的首尾。总时间复杂度为 \(O(n \log n)\),瓶颈在排序。
代码精讲
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=1e6+10;
int n;
ull ans; // mod 2^64 即 unsigned long long 自然溢出
int a[N],b[N];
map<int,int> mp; // 桶
int main(){
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",a+i);
for (int i=1;i<=n;++i) scanf("%d",b+i);
for (int i=1;i<=n;++i) ++mp[a[i]];
vector<int> vec; // 存储 +1 次数,用于分配 b[i]
stack<pii> stk; // 存储块
stk.push({INT_MAX,INT_MAX});
for (auto i=mp.rbegin();i!=mp.rend();++i){
if (stk.top().second!=i->first) stk.top().first=i->first+1;
for (int j=1;j<i->second;++j){
while (stk.top().first>stk.top().second) stk.pop();
vec.push_back(stk.top().first-i->first),++stk.top().first;
}
stk.push({i->first,i->first-1});
}
sort(vec.begin(),vec.end(),greater<int>());
sort(b+1,b+n+1);
int l=vec.size();
for (int i=0;i<l;++i) ans+=vec[i]*1ull*b[i+1];
printf("%llu",ans);
return 0;
}

浙公网安备 33010602011771号