[NOIP 2024 模拟11]钱
[NOIP 2024 模拟11]钱
题意:
众所周知,y喜欢买奶茶,却不喜欢喝奶茶。但是他买的时候总是没时间找到最大的优惠。
y有无限张价值 𝑚的奶茶代金券,每次y会使用代金券购买两杯奶茶。
只有当代金券的总价值大于等于奶茶的总价值才可以购买,但是奶茶店是不找零的。
举个栗子。
假设每张代金券价值 10 元,然后买了一杯 11 元和一杯 4 元的奶茶。
则需要两张代金券才能购买,但是两张代金券价值 20,奶茶总价值 15,即我们可以认为y这样做浪费了 5 元。
现在,强强帮必须伸出援手了,已知y总共购买了 𝑛 种价值的奶茶,第 𝑖 种奶茶购买的数量为 𝑎𝑖,价格为 𝑏𝑖。
请问y最少浪费多少钱?
输入第一行包含两个正整数 𝑛, 𝑚,表示共有 𝑛 条信息,每张代金券价值 𝑚 元。
(1 ≤ 𝑛≤ 10^5, 1 ≤ 𝑚 ≤ 10^9 )
接下来 𝑛行每行包含两个正整数 𝑎𝑖, 𝑏𝑖(1 ≤ 𝑎𝑖, 𝑏𝑖 ≤ 10^9),
保证给出的所有 𝑏𝑖 不会重复,且所有 𝑎 之和为一个小于 1e9 的偶数。
思路:
我们首先思考缩小 b_i 的范围;
我们设 b_i = m*p + q (m>=0),发现 m*p 的部分可以被若干张代金券抵消,而 q 这部分就不确定了;
所以我们把 b_i 处理成 q,然后进行下一步处理;
————————————————————————————————————————————————————————————————————————————————————————————————————————
显然,如果我们一杯一杯买,那么代价就是 Sum(i=1~n) a_i * (m - (b_i % p))
我们再思考,两杯两杯地买会不会让代价减少:
一,若 (b_i % p + b_j % p) > m,那么代价就是 (2*m - (b_i % p + b_j % p));
我们会发现 上述代价等价于 (m - (b_i % p)) + (m - (b_j % p)),即等价于买两次单杯。这样我们并没有减少代价;
二,若 (b_i % p + b_j % p) <= m,那么代价就是 (m - (b_i % p + b_j % p));
而这个代价 是小于 买两次单杯的总代价:(m - (b_i % p)) + (m - (b_j % p)) 的。
所以综上所述,我们要让符合第二种条件的价格两两匹配,而符合条件的价格有两种组合形式:
(我们把 b_i <= m/2 的叫做 “小的”,把 b_i > m/2 的叫做 “大的”)
1. b_i + b_j <= m ( b_i <= m/2 && b_j >= m/2 ) (小的 + 大的)
2. b_i + b_j <= m ( b_i <= m/2 && b_j <= m/2 ) (小的 + 小的)
———————————————————————————————————————————————————————————————————————————————————————————————————————————
接下来就是代码实现,注意看 while() 循环部分、处理可能剩余的 “大的” “小的”的部分、处理 b_i == 0 的情况 !
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
#define int long long
int n,m,ans;
struct data1{
int cnt,val;
}a[N];
data1 xiao[N]; int top1=0;
data1 da[N]; int top2=0;
bool cmp1(data1 x,data1 y) {
return x.val>y.val;
}
bool cmp2(data1 x,data1 y) {
return x.val<y.val;
}
signed main() {
freopen("qian.in","r",stdin);
freopen("qian.out","w",stdout);
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%lld%lld",&a[i].cnt,&a[i].val);
a[i].val%=m;
// if(a[i].val==0){
// n--,i--;
// continue;
// }
if(a[i].val<m/2) {
xiao[++top1]=a[i];
}
else {
da[++top2]=a[i];
}
}
sort(xiao+1,xiao+1+top1,cmp1);
sort(da+1,da+1+top2,cmp2);
int p_xiao=1,p_da=1;
while(p_xiao<=top1 && p_da<=top2) {
while(xiao[p_xiao].val+da[p_da].val>m && p_xiao<=top1) p_xiao++;
if(p_xiao>top1) break;
int geshu=min(xiao[p_xiao].cnt,da[p_da].cnt);
ans+=geshu*(m-xiao[p_xiao].val-da[p_da].val);
xiao[p_xiao].cnt-=geshu;
da[p_da].cnt-=geshu;
if(!xiao[p_xiao].cnt) p_xiao++;
if(!da[p_da].cnt) p_da++;
if(p_xiao>top1) break;
if(p_da>top2) break;
}
if(p_da<=top2) {
for(int i=p_da;i<=top2;i++) {
ans+=(m-da[i].val)*da[i].cnt;
}
}
int num=0,sum=0;
for(int i=1;i<=top1;i++) {
if(xiao[i].cnt && xiao[i].val==0) continue;
num+=xiao[i].cnt;
sum+=xiao[i].cnt*xiao[i].val;
}
int geshu=ceil(num*1.0/2);
ans+=geshu*m-sum;
printf("%lld",ans);
return 0;
}