CF1607G Banquet Preparations 1 题解

8 篇文章 0 订阅
1 篇文章 0 订阅

CF1607G 题解

RabbieWjy


传送门

https://codeforces.com/problemset/problem/1607/G

https://www.luogu.com.cn/problem/CF1607G

题意

此题的一般难点在读题 ,本人读了三四次,每一次都发现之前理解有错,建议先自己尝试理解题目,提高阅读理解能力(

n n n 道菜品,第 i i i 道菜品有 a i a_i ai 克鱼和 b i b_i bi 克肉。

定义这些菜品的平衡值 ∣ ∑ i = 1 n a i − ∑ i = 1 n b i ∣ \left| \sum \limits_{i=1} ^n a_i - \sum \limits_{i=1} ^n b_i \right| i=1naii=1nbi,平衡值越小越好。

现在,一位吃货被邀请吃掉每一道菜的 m m m 克。具体地说,对于第 i i i 道菜,他可以吃掉 x i x_i xi 克鱼和 y i y_i yi 克肉,使得 x i + y i = m x_i + y_i=m xi+yi=m

问这位吃货要怎么吃,使得剩下的菜品平衡值最小,输出任意一种方案。

格式化题面

给你两个序列 a i a_i ai b i b_i bi,求 x i x_i xi y i y_i yi 使得 x i + y i = m x_i+y_i=m xi+yi=m 0 ≤ x i ≤ a i 0 \leq x_i \leq a_i 0xiai 0 ≤ y i ≤ b i 0 \leq y_i \leq b_i 0yibi ∣ ∑ i = 1 n ( a i − x i ) − ∑ i = 1 n ( b i − y i ) ∣ \left| \sum \limits_{i=1} ^n (a_i-x_i) - \sum\limits_{i=1} ^n (b_i-y_i) \right| i=1n(aixi)i=1n(biyi) 最小。

思路

首先,枚举是肯定不能过的。

其次,我们看一下数据范围,想到正解的复杂度应为 O ( n ) O(n) O(n)

题目问平衡值的最小值,想到贪心。

一个不太对的贪心

要使平衡值最小,就要让 ∑ i = 1 n ( a i − x i ) \sum \limits_{i=1} ^n (a_i-x_i) i=1n(aixi) ∑ i = 1 n ( b i − y i ) \sum \limits_{i=1} ^n (b_i-y_i) i=1n(biyi) 尽量接近。

那么分别统计鱼和肉的总量,哪边多就吃那边就好了。

但是由于有 m m m 的限制,这个思路我就没有想下去。

正解

让我们先看一看题目中的这个式子,把它化简一下:

a n s = min ⁡ ∣ ∑ i = 1 n ( a [ i ] − x [ i ] ) − ∑ i − 1 n ( b [ i ] − y [ i ] ) ∣ = min ⁡ ∣ ∑ i − 1 n ( a [ i ] − x [ i ] ) − ∑ i = 1 n ( b [ i ] − m + x [ i ] ) ∣ = min ⁡ ∣ ∑ i = 1 n ( a [ i ] − b [ i ] + m ) − 2 × ∑ i = 1 n x [ i ] ∣ = min ⁡ ∣ X − Y ∣ ans = \min \left| \sum \limits_{i=1} ^n (a[i]-x[i]) -\sum \limits_{i-1} ^n (b[i]-y[i]) \right| \\ = \min \left| \sum \limits_{i-1} ^n(a[i]-x[i]) - \sum \limits_{i=1} ^n (b[i]-m + x[i]) \right| \\ = \min \left| \sum \limits_{i=1} ^n (a[i]-b[i]+m) - 2 \times \sum \limits_{i=1} ^n x[i] \right| \\ = \min \left| X-Y \right| ans=mini=1n(a[i]x[i])i1n(b[i]y[i])=mini1n(a[i]x[i])i=1n(b[i]m+x[i])=mini=1n(a[i]b[i]+m)2×i=1nx[i]=minXY

左边是一个定值,右边有范围: L = 2 × ∑ i = 1 n ( m − min ⁡ ( m , b [ i ] ) ) ≤ Y ≤ 2 × ∑ i = 1 n min ⁡ ( m , a [ i ] ) = R L = 2 \times \sum \limits_{i=1} ^n (m-\min(m,b[i])) \leq Y \leq 2 \times \sum \limits_{i=1} ^n \min(m,a[i]) = R L=2×i=1n(mmin(m,b[i]))Y2×i=1nmin(m,a[i])=R

那么 a n s ans ans [ L , R ] [L,R] [L,R] 中最接近 X X X 的数,分三种情况讨论:

  1. L > X L > X L>X:则 a n s = L − X ans=L-X ans=LX,即 y [ i ] = m i n ( m , b [ i ] ) , x [ i ] = m − y [ i ] y[i]=min(m,b[i]),x[i]=m-y[i] y[i]=min(m,b[i]),x[i]=my[i]
  2. R < X R < X R<X:则 a n s = X − R ans=X-R ans=XR,即 x [ i ] = m i n ( m , a [ i ] ) , y [ i ] = m − x [ i ] x[i]=min(m,a[i]),y[i]=m-x[i] x[i]=min(m,a[i]),y[i]=mx[i]
  3. L ≤ X ≤ R L \leq X \leq R LXR:则 a n s = 0 / 1 ans = 0 /1 ans=0/1(取决于 X X X 的奇偶性),此时贪心地取 x [ i ] x[i] x[i],使 Y Y Y 尽量靠近 X X X

代码实现

#include<iostream>
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;

int t;
int n,m;
int a[200010],b[200010],A[200010],X,L,R;
int x[200010];

signed main()
{
	scanf("%lld",&t);
	while (t --)
	{
		X = L = R = 0;
		scanf("%lld%lld",&n,&m);
		for (int i = 1;i <= n;i ++)
		{
			scanf("%lld%lld",a + i,b + i);
			X += a[i] - b[i] + m;
			R += 2 * min(m,a[i]);
			L += 2 * (m - min(m,b[i]));
		}
		if (X < L)
		{
			printf("%lld\n",L - X);
			for (int i = 1;i <= n;i ++) printf("%lld %lld\n",m - min(m,b[i]),min(m,b[i]));
		}
		else if (X > R)
		{
			printf("%lld\n",X - R);
			for (int i = 1;i <= n;i ++) printf("%lld %lld\n",min(m,a[i]),m - min(m,a[i]));
		}
		else
		{
			int cnt = 0;
			for (int i = 1;i <= n;i ++) x[i] = (b[i] < m ? m - b[i] : 0ll),cnt += 2 * x[i],A[i] = a[i] - x[i];
			for (int i = 1;i <= n;i ++)
			{
				if (cnt >= X) break;
				if (cnt + 2 * min(m - x[i],A[i]) >= X)
				{
					x[i] += (X - cnt) / 2;
					cnt += (X - cnt) / 2 * 2;
					break;
				}
				cnt += 2 * (min(m,a[i]) - x[i]);
				x[i] = min(m,a[i]);
			}
			printf("%lld\n",(cnt - X < 0 ? X - cnt : cnt - X));
			for (int i = 1;i <= n;i ++) printf("%lld %lld\n",x[i],m - x[i]);
		}
	}
}
  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值