洛谷 P6155 修改 题解

题目链接

洛谷 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;
}
posted @ 2025-11-30 19:13  CodingJuRuo  阅读(0)  评论(0)    收藏  举报