刚读题想到用dp,思考一下发现dp并不是那么容易。然后脑补出来了贪心做法:枚举每一个长度 l 使得最终的稳定态是以 l 为最长腿,那么所有比 l 大的桌腿我们都要砍掉,然后所有 l 的都不砍,最后把比 l 小的若干个 d 比较小的砍掉之后使得 l 的个数大于其他桌腿的个数。
具体实现的时候,我们先把桌腿按照 l 升序排列,然后预处理出来cnt[i],first[i],两个个数组。其中cnt[i]表示桌腿长度为i的个数,first[i]表示长度为 i 的桌腿第一次出现在数列中的位置。然后枚举桌腿的长度,求出最小的花费即可。
枚举桌腿后求花费的时候我们需要找到区间前k小数。这里用d[i][j]数组预处理下。d[i][j]数组表示前 i 个桌腿 d 为 j 的个数,然后就可以o(dmax=200)的时间内找到前k小数。
#pragma warning(disable:4996)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
struct line{
int l, d;
bool operator<(const line&op)const{
return l < op.l;
}
};
line a[100005];
int cnt[100005] = { 0 };//标记l出现的个数
LL sum[100005] = { 0 };//d的前n项和
int first[100005] = { 0 };//l最早出现的位置
int d[100005][201] = { 0 };
int main(){
int n; scanf("%d", &n);
for (int i = 1; i <= n; i++){
scanf("%d", &a[i].l);
cnt[a[i].l]++;
}
for (int i = 1; i <= n; i++){
scanf("%d", &a[i].d);
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++){
sum[i] = sum[i - 1] + a[i].d;
if (first[a[i].l] == 0)first[a[i].l] = i;
for (int j = 1; j < 201; j++){
d[i][j] = d[i - 1][j];
}
d[i][a[i].d]++;
}
LL ans = 1000000000000LL;
for (int i = 1; i <= 100000; i++){
if (cnt[i]){
int pos = first[i] + cnt[i] - 1;
LL now = sum[n] - sum[pos];
int qudiao = first[i] - cnt[i];
if (qudiao > 0){
for (int j = 1; j < 201; j++){
if (qudiao >= d[first[i] - 1][j]){
now += d[first[i] - 1][j] * j;
qudiao -= d[first[i] - 1][j];
}
else{
now += qudiao*j;
break;
}
}
}
ans = min(ans, now);
}
}
printf("%I64d\n", ans);
return 0;
}