codeforces-RaifRound1-E

codeforces-RaifRound1-E

E. Carrots for Rabbits

  • 题意

给出n<1e5个萝卜每个萝卜的长度为$a_i$<1e6

现在要分给k个兔子(k>=n&&k<1e5) 你能将萝卜切成长度不同的小份

使得每个兔子能分到一段萝卜

它们吃萝卜所消耗的时间为其分到萝卜长度的平方即$len^2$

求它们吃萝卜所消耗的时间和(注意是$∑len_i^2$)最少是多少

  • 题解

参考官方题解

首先定义一个函数$f(len,pieces)$

表示将一根长度为len的胡萝卜分成pieces份 后给兔子吃所需的最小总时间

  • 对于一根萝卜如何求它的$f(len,pieces)$ ?

当pieces=2,我们分成等长的两段(或长度尽量接近)所需的总时间最小

当pieces=3,我们分成等长的三段(或长度尽量接近)所需的总时间最小

因为$f(len,pieces)=∑_i^{pieces} len_i^2$ 最长的那段取平方后总会产生较大的时间消耗

所以直觉上均分能获得最优解(所需总时间最小)

  • 这个规律即:对一个sum分成若干份每份取等次幂(推测)求和总是均分更优

我们需要切k-n刀才够每个兔子分 每次切能减小总时长最大的那个萝卜

即$f(len,pieces)-f(len,pieces+1)$ 最大的那个萝卜

于是用优先队列每次切一刀取出$f(len,pieces)-f(len,pieces+1)$最大的那个萝卜

到这里这题就可以解决了

但是题解中提到$f(l,p−1)−f(l,p)≥f(l,p)−f(l,p+1) $

其含义是 对同一根萝卜多次切 所能减少的总时间是非增的 (逐渐减少)

用数字表示就是8可以分成4 4和2 2 2 2

8 -> 4 4 减少的总时间小于 4 4 -> 2 2 2 2 减少的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
#define P pair<ll,int>
#define Turnoff std::ios::sync_with_stdio(false)
const ll Max=1e5+5;
const int Mod=998244353;

/*
*/
int n,k;
ll arr[Max],Pi[Max],NeedTime[Max];
ll cut(int len,int pieces){
ll Newlen=len/pieces;
ll Pieces_NewlenAddone=len%pieces;
ll Pieces_Newlen=pieces-Pieces_NewlenAddone;
ll tot=Newlen*Newlen*Pieces_Newlen+(Newlen+1)*(Newlen+1)*Pieces_NewlenAddone;
return tot;
}
int main() {
Turnoff;
cin>>n>>k;
ll sum=0;k=k-n;
priority_queue<P,vector<P>,less<P> >q;
for(int i=0;i<n;i++){
cin>>arr[i];Pi[i]=1;
sum+=arr[i]*arr[i];
q.push({arr[i]*arr[i]-cut(arr[i],2),i});
NeedTime[i]=cut(arr[i],2);
}
while(k--){
P temp=q.top();q.pop();
int i=temp.second;ll cost=temp.first;
sum-=cost;Pi[i]++;
//cout<<cut(arr[i],Pi[i]+1)<<endl;
q.push({NeedTime[i]-cut(arr[i],Pi[i]+1),i});
NeedTime[i]=cut(arr[i],Pi[i]+1);
}
cout<<sum<<endl;
}