【推公式法】【线性代数】P2512 [HAOI2008]糖果传递

739 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

原题地址

提交 10.41k
通过 4.81k
时间限制 1.00s
内存限制 125.00MB

题目描述

nn 个小朋友坐成一圈,每人有 aia_i 个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 11

输入格式

小朋友个数 nn,下面 nnaia_i

输出格式

求使所有人获得均等糖果的最小代价。

输入输出样例

输入 #1

4
1
2
5
4

输出 #1

4

说明/提示 对于 100%100\% 的数据 n106n\le 10^6


思路:

设每个人原来拥有糖果数量为 AiA_i ,每次向右传递数量为 XiX_i (向左为 Xi-X_i ),糖果平均数为 avgavg

则有

原有的向右传出去的+左边传进来的=平均数原有的-向右传出去的+左边传进来的=平均数

AiXi+Xi+1=avgA_i-X_i+X_{i+1}=avg

联立方程

{A1X1+X2=avgA2X2+X3=avgAn1Xn1+Xn=avgAnXn+X1=avg\left\{ \begin{aligned} A_1-X_1+X_{2}&=avg \\ A_2-X_2+X_{3}&=avg \\ \cdots \\ A_{n-1}-X_{n-1}+X_{n}&=avg \\ A_n-X_n+X_{1}&=avg \\ \end{aligned} \right.

整理得

{X1+X2=avgA1X2+X3=avgA2Xn1+Xn=avgAn1X1Xn=avgAn\left\{ \begin{aligned} -X_1+X_{2}&=avg-A_1 \\ -X_2+X_{3}&=avg-A_2 \\ \cdots \\ -X_{n-1}+X_{n}&=avg-A_{n-1} \\ X_{1}-X_n&=avg-A_n \\ \end{aligned} \right.

其增广矩阵

B=(A    β)=[11000aveA101100aveA200011aveAn110001aveAn]B=(A \;\;\beta)= \left[ \begin{array}{cccccc|c} -1 & 1 & 0 & \cdots & 0 & 0 & ave-A_1\\ 0 & -1 & 1 & \cdots & 0 & 0 & ave-A_2\\ \vdots & & \ddots& & & \vdots & \vdots\\ 0 & 0 & 0 & \cdots & -1 & 1 & ave-A_{n-1}\\ 1 & 0 & 0 & \cdots & 0 & -1 & ave-A_{n}\\ \end{array} \right]

化为行最简形

[10001(i=1n1Ai)(n1)ave01001(i=2n1Ai)(n2)ave00011An1ave000000]\left[ \begin{array}{cccccc|c} 1 & 0 & 0 & \cdots & 0 & -1 & (\sum_{i=1}^{n-1}A_i)-(n-1)ave\\ 0 & 1 & 0 & \cdots & 0 & -1 & (\sum_{i=2}^{n-1}A_i)-(n-2)ave\\ \vdots & & \ddots& & & \vdots & \vdots\\ 0 & 0 & 0 & \cdots & 1 & -1 & A_{n-1}-ave\\ 0 & 0 & 0 & \cdots & 0 & 0 & 0\\ \end{array} \right]

得方程组的解为

{X1=Xn+(i=1n1Ai)(n1)aveX2=Xn+(i=2n1Ai)(n2)aveXn1=Xn+An1aveXn=XnR\left\{ \begin{aligned} X_1&=X_n+(\sum_{i=1}^{n-1}A_i)&-(n-1)ave\\ X_2&=X_n+(\sum_{i=2}^{n-1}A_i)&-(n-2)ave\\ &\qquad \qquad \qquad \vdots \\ X_{n-1}&=X_n+A_{n-1}&-ave\\ X_n&=X_n&\in{\Bbb R}\\ \end{aligned} \right.

现在要求 i=1nXi\sum_{i=1}^{n}|X_i| 的最小值

观察方程组的解,发现所有项都有相同的变量 XnX_n ,所以构造函数

Xx=Xn+f(x)X_x=X_n+f(x)

f(x)=i=xn1Ai  (nx)avef(x)=\sum_{i=x}^{n-1}A_i \; -(n-x)ave

所以

i=1nXi=Xn+f(1)++Xn+f(n)=i=1nXn+f(i)\sum_{i=1}^{n}|X_i|=|X_n+f(1)|+\cdots+|X_n+f(n)|=\sum_{i=1}^{n}|X_n+f(i)|

i=1nXn+f(i)\sum_{i=1}^{n}|X_n+f(i)| 可以看作数轴上的点 XnX_n 到点 f(i),i[1,n]-f(i),i\in [1,n] 的距离之和

那么 XnX_n 应取最中间的点(高中结论,函数图像为平底锅( nn 为偶数)或尖底锅( nn 为奇数))

于是我们只要找到 f(i),i[1,n]-f(i),i\in [1,n] 的中位数,把他赋值给 XnX_n 即可 将其代入i=1nXn+f(i)\sum_{i=1}^{n}|X_n+f(i)| 即为本题答案

PS:PS: f(x)f(x) 有递归求法 f(i)=f(i+1)+Aiavef(i)=f(i+1)+A_i-ave ,从后往前循环即可,不要用 f(x)=i=xn1Ai  (nx)avef(x)=\sum_{i=x}^{n-1}A_i \; -(n-x)ave ,后者会导致 TLETLE


ACAC 代码

R73269788 记录详情

编程语言 C++20
代码长度 494B
用时 2.34s
内存 8.05MB
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
using namespace std;

const int N = 1e6+1;
int n,a[N],f[N];
ll ave=0,ans=0,Xn;

int main(){
    cin>>n;
    FOR(i,1,n){
        cin>>a[i];
        ave+=a[i];
    }
    ave/=n;//由题意,一定能整除
    f[n]=0;
    ROF(i,n-1,1)
        f[i]=f[i+1]+a[i]-ave;
    sort(f+1,f+n+1);
    Xn=-f[(n+1)/2];
    FOR(i,1,n)
        ans+=abs(Xn+f[i]);
    cout<<ans;
    return 0;
}