[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;
}
posted on 2024-11-07 18:45  Ueesugi_sakura  阅读(1)  评论(0编辑  收藏  举报