题目链接
题意
给你 n n n个数 a i a_i ai,要你在满足下面条件下使得 ∑ i = 1 n ( a i − p i ) 2 \sum\limits_{i=1}^{n}(a_i-p_i)^2 i=1∑n(ai−pi)2最小(题目给的 m m m只是为了将 a i a_i ai变成一个整数,那么我们就当此处的 p i p_i pi扩大为题目给的 m m m倍,然后把 m m m放到分母去,以下不再解释):
- p i ∈ R p_i\in\mathbb{R} pi∈R;
- p i ≥ 0 , i ∈ [ 1 , n ] p_i\geq 0,i\in[1,n] pi≥0,i∈[1,n];
- ∑ i = 1 n p i = m \sum\limits_{i=1}^{n}p_i=m i=1∑npi=m。
思路
由于叉姐的题解太高深了,本菜鸡完全看不懂(爆哭),因此我们从其他角度来求解本题。
首先根据题目要求的式子和条件可以发现我们能做的只是将
p
i
p_i
pi合理赋值使得
∑
i
=
1
n
(
a
i
−
p
i
)
2
\sum\limits_{i=1}^{n}(a_i-p_i)^2
i=1∑n(ai−pi)2最小,且
a
i
∈
[
−
m
,
m
]
,
p
i
∈
[
0
,
m
]
a_i\in[-m,m],p_i\in[0,m]
ai∈[−m,m],pi∈[0,m],那么
p
i
p_i
pi只能将
a
i
a_i
ai的值不断减小而不能增加(即使
a
i
<
0
a_i<0
ai<0),因此我们就可以通过调节
p
i
p_i
pi的值使得
a
i
a_i
ai的最大值尽可能的小,且
∑
i
=
1
n
p
i
=
m
\sum\limits_{i=1}^{n}p_i=m
i=1∑npi=m。
假设我们进行处理前
i
−
1
i-1
i−1后
p
p
p的和还剩下
l
a
s
las
las,前
i
i
i个的
a
a
a的值都已经被削到了
a
i
a_i
ai,那么:
- 如果 i × ∣ ( a [ i + 1 ] − a [ i ] ) ∣ ≤ l a s i\times|(a[i+1]-a[i])|\leq las i×∣(a[i+1]−a[i])∣≤las,那么 l a s = l a s − i × ∣ ( a [ i + 1 ] − a [ i ] ) ∣ las=las-i\times|(a[i+1]-a[i])| las=las−i×∣(a[i+1]−a[i])∣;
- 否则,就记录这个位置为 i d x idx idx,并且 b r e a k break break( i d x idx idx的初始值为 n n n)。
其中的
i
d
x
idx
idx就是说我们可以通过调节
p
i
p_i
pi的值使得前
i
d
x
idx
idx个数都相等且等于
a
i
d
x
−
l
a
s
i
d
x
a_{idx}-\frac{las}{idx}
aidx−idxlas,因此最后答案是
i
d
x
×
(
a
i
d
x
−
l
a
s
i
d
x
)
2
+
∑
i
=
i
d
x
+
1
n
a
i
d
x
2
m
2
\frac{idx\times(a_{idx}-\frac{las}{idx})^2+\sum\limits_{i=idx+1}^{n}a_{idx}^2}{m^2}
m2idx×(aidx−idxlas)2+i=idx+1∑naidx2(因为我们最初始时将
a
i
,
p
i
a_i,p_i
ai,pi都扩大了
m
m
m倍,将
m
m
m丢到了分母)。
如果没看懂我们可以通过分析样例
3
3
3来帮助理解:
首先我们将
a
a
a数组排序得到:
3
3
3
1
1
1
−
2
-2
−2
- 处理前 1 1 1个数,此时 l a s = 10 > 1 × ∣ 1 − 3 ∣ = 2 las=10>1\times|1-3|=2 las=10>1×∣1−3∣=2,于是我们将 a 1 a_1 a1变成 1 1 1, l a s las las消耗 2 2 2;
- 处理前 2 2 2个数,此时 l a s = 8 > 2 × ∣ − 2 − 1 ∣ = 6 las=8>2\times|-2-1|=6 las=8>2×∣−2−1∣=6,于是我们将 a 1 , a 2 a_1,a_2 a1,a2变成 − 2 -2 −2, l a s las las消耗 6 6 6;
- 处理前 3 3 3个数,此时 l a s = 2 , a 1 = a 2 = a 3 = − 2 las=2,a_1=a_2=a_3=-2 las=2,a1=a2=a3=−2,由于 l a s las las最后要变成 0 0 0且 a i a_i ai的最大值要尽可能小,那么我们需要均匀分配,所以最后 a 1 = a 2 = a 3 = − 2 − 2 3 = − 8 3 a_1=a_2=a_3=-2-\frac{2}{3}=-\frac{8}{3} a1=a2=a3=−2−32=−38。
所以最后答案为
(
−
8
3
)
2
×
3
10
×
10
=
16
75
\frac{(-\frac{8}{3})^2\times 3}{10\times 10}=\frac{16}{75}
10×10(−38)2×3=7516。
u
p
d
a
t
e
update
update:
证明这个写法的正确性:
假设现在有两个数 a , b ( a ≥ b ) a,b(a\geq b) a,b(a≥b),总的可以减少的数量为 m m m。
1.首先证明当 a − b ≥ m a-b\geq m a−b≥m时全放在 a a a上最优:
设
m
m
m中有
x
(
1
≤
x
≤
m
)
x(1\leq x\leq m)
x(1≤x≤m)用在
b
b
b上,那么和为
(
a
−
m
+
x
)
2
+
(
b
−
x
)
2
(a-m+x)^2+(b-x)^2
(a−m+x)2+(b−x)2,全用在
a
a
a上的话和为
(
a
−
m
)
2
+
b
2
(a-m)^2+b^2
(a−m)2+b2,两者做差:
(
a
−
m
+
x
)
2
+
(
b
−
x
)
2
−
(
a
−
m
)
2
−
b
2
=
(
a
−
m
)
2
+
2
x
(
a
−
m
)
+
x
2
+
b
2
−
2
b
x
+
x
2
−
(
a
−
m
)
2
−
b
2
=
2
x
2
+
2
x
(
a
−
m
)
−
2
b
x
=
2
x
2
+
2
x
(
a
−
b
−
m
)
≥
0
\begin{aligned} &(a-m+x)^2+(b-x)^2-(a-m)^2-b^2&\\ =&(a-m)^2+2x(a-m)+x^2+b^2-2bx+x^2-(a-m)^2-b^2&\\ =&2x^2+2x(a-m)-2bx&\\ =&2x^2+2x(a-b-m)\geq 0& \end{aligned}
===(a−m+x)2+(b−x)2−(a−m)2−b2(a−m)2+2x(a−m)+x2+b2−2bx+x2−(a−m)2−b22x2+2x(a−m)−2bx2x2+2x(a−b−m)≥0
2.再证明当 a = b , m ≥ 0 a=b,m\geq 0 a=b,m≥0时均分最优:
设
m
m
m中有
x
(
1
≤
x
<
m
2
)
x(1\leq x< \frac{m}{2})
x(1≤x<2m)用在
b
b
b上,那么和为
(
a
−
m
+
x
)
2
+
(
b
−
x
)
2
(a-m+x)^2+(b-x)^2
(a−m+x)2+(b−x)2,将其化简:
(
a
−
m
+
x
)
2
+
(
b
−
x
)
2
=
(
a
−
m
)
2
+
2
(
a
−
m
)
x
+
x
2
+
b
2
−
2
b
x
+
x
2
=
(
a
−
m
)
2
+
b
2
+
2
x
2
+
2
(
a
−
m
−
b
)
x
=
(
a
−
m
)
2
+
b
2
+
2
(
x
2
−
m
x
)
(
题目给定的
a
=
b
)
\begin{aligned} &(a-m+x)^2+(b-x)^2&\\ =&(a-m)^2+2(a-m)x+x^2+b^2-2bx+x^2&\\ =&(a-m)^2+b^2+2x^2+2(a-m-b)x&\\ =&(a-m)^2+b^2+2(x^2-mx)(\text{题目给定的}a=b)& \end{aligned}
===(a−m+x)2+(b−x)2(a−m)2+2(a−m)x+x2+b2−2bx+x2(a−m)2+b2+2x2+2(a−m−b)x(a−m)2+b2+2(x2−mx)(题目给定的a=b)
由于前一半和
x
x
x无关,而后一半
x
2
−
m
x
=
(
x
−
m
2
)
2
−
m
2
4
x^2-mx=(x-\frac{m}{2})^2-\frac{m^2}{4}
x2−mx=(x−2m)2−4m2在
x
=
m
2
x=\frac{m}{2}
x=2m时取最小值。
最后我们来讨论有 n n n个数分配 m m m,其中 a , b a,b a,b分别为 n n n个数中的最大和次大值时为什么当 m > a − b m>a-b m>a−b时每次将最大值减小到次大值是最优的的情况:
- ( 1 ) . (1). (1).首先如果将 m m m全部减到一个数上,那么肯定是减小最大值是最优的: ( a − m ) 2 + c 2 − a 2 − ( c − m ) 2 = a 2 − 2 a m + m 2 + c 2 − a 2 − c 2 + 2 c m − m 2 = 2 m ( c − a ) < 0 ( c (a-m)^2+c^2-a^2-(c-m)^2=a^2-2am+m^2+c^2-a^2-c^2+2cm-m^2=2m(c-a)<0(c (a−m)2+c2−a2−(c−m)2=a2−2am+m2+c2−a2−c2+2cm−m2=2m(c−a)<0(c为剩余 n − 1 n-1 n−1个数中的任意一个数);
- ( 2 ) . (2). (2).如果分配到两个数上那么也一定时分到最大值和次大值上,理由同上;那么此时我们应该怎么分配最优呢?我们发现当 a a a减少到 a = b a=b a=b之后如果再减少 a a a的话, b b b就已经大于 a a a成为新的最大值了,那么再减小 a a a肯定不是最优解(理由为 ( 1 ) (1) (1)),因此只能将最大值 a a a减小到次大值 b b b,然后根据上面的 2 2 2得知均匀分配最优;
- 分配到任意多个数的情况基本和 ( 2 ) (2) (2)相同。
因此我们可以得知本题的解决策略是正确的。
(如果想法或者证明过程有错误,还请各位大佬指正~)
代码实现如下
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> pil;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("D://Code//in.txt","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)
const double eps = 1e-8;
const int mod = 1000000007;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int n, m;
int a[maxn];
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
while(~scanf("%d%d", &n, &m)) {
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
sort(a + 1, a + n + 1, [](int a, int b){return a > b;});
int idx = n;
int las = m;
for(int i = 1; i < n; ++i) {
if(i * abs(a[i+1] - a[i]) > las) {
idx = i;
break;
} else {
las -= i * abs(a[i+1] - a[i]);
}
}
LL num1 = 1LL * (idx * a[idx] - las) * (idx * a[idx] - las);
LL num2 = 1LL * idx * m * m;
for(int i = idx + 1; i <= n; ++i) {
num1 += 1LL * a[i] * a[i] * idx;
}
LL tmp = __gcd(num1, num2);
num1 /= tmp, num2 /= tmp;
if(num2 == 1) printf("%lld\n", num1);
else printf("%lld/%lld\n", num1, num2);
}
return 0;
}