题目链接
题意
给你个数,要你在满足下面条件下使得最小(题目给的只是为了将变成一个整数,那么我们就当此处的扩大为题目给的倍,然后把放到分母去,以下不再解释):
- ;
- ;
- 。
思路
由于叉姐的题解太高深了,本菜鸡完全看不懂(爆哭),因此我们从其他角度来求解本题。
首先根据题目要求的式子和条件可以发现我们能做的只是将合理赋值使得最小,且,那么只能将的值不断减小而不能增加(即使),因此我们就可以通过调节的值使得的最大值尽可能的小,且。
假设我们进行处理前后的和还剩下,前个的的值都已经被削到了,那么:
- 如果,那么;
- 否则,就记录这个位置为,并且(的初始值为)。
其中的就是说我们可以通过调节的值使得前个数都相等且等于,因此最后答案是(因为我们最初始时将都扩大了倍,将丢到了分母)。
如果没看懂我们可以通过分析样例来帮助理解:
首先我们将数组排序得到:
- 处理前个数,此时,于是我们将变成,消耗;
- 处理前个数,此时,于是我们将变成,消耗;
- 处理前个数,此时,由于最后要变成且的最大值要尽可能小,那么我们需要均匀分配,所以最后。
所以最后答案为。 :
证明这个写法的正确性:
假设现在有两个数,总的可以减少的数量为。
1.首先证明当时全放在上最优:
设中有用在上,那么和为,全用在上的话和为,两者做差:
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; }