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=1∑nai−i=1∑nbi∣∣∣∣,平衡值越小越好。
现在,一位吃货被邀请吃掉每一道菜的 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 0≤xi≤ai, 0 ≤ y i ≤ b i 0 \leq y_i \leq b_i 0≤yi≤bi 且 ∣ ∑ 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=1∑n(ai−xi)−i=1∑n(bi−yi)∣∣∣∣ 最小。
思路
首先,枚举是肯定不能过的。
其次,我们看一下数据范围,想到正解的复杂度应为 O ( n ) O(n) O(n)。
题目问平衡值的最小值,想到贪心。
一个不太对的贪心
要使平衡值最小,就要让 ∑ i = 1 n ( a i − x i ) \sum \limits_{i=1} ^n (a_i-x_i) i=1∑n(ai−xi) 和 ∑ i = 1 n ( b i − y i ) \sum \limits_{i=1} ^n (b_i-y_i) i=1∑n(bi−yi) 尽量接近。
那么分别统计鱼和肉的总量,哪边多就吃那边就好了。
但是由于有 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=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∑nx[i]∣∣∣∣=min∣X−Y∣
左边是一个定值,右边有范围: 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=1∑n(m−min(m,b[i]))≤Y≤2×i=1∑nmin(m,a[i])=R
那么 a n s ans ans 为 [ L , R ] [L,R] [L,R] 中最接近 X X X 的数,分三种情况讨论:
- L > X L > X L>X:则 a n s = L − X ans=L-X ans=L−X,即 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]=m−y[i];
- R < X R < X R<X:则 a n s = X − R ans=X-R ans=X−R,即 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]=m−x[i];
- L ≤ X ≤ R L \leq X \leq R L≤X≤R:则 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]);
}
}
}