CF1428E. Carrots for Rabbits
题意:
给你 n n n个正整数,分成 k k k个正整数,要求分好之后 ∑ i = 1 k a i 2 \sum\limits_{i=1}^{k}{a_i}^{2} i=1∑kai2最小。
思路:
可以想到将
a
i
a_i
ai均分会使
a
i
a_i
ai的和减少的最多。假设我们把
a
a
a分成
b
,
c
b,c
b,c两份,那么:
a
2
−
(
b
2
+
c
2
)
=
(
b
+
c
)
2
−
(
b
2
+
c
2
)
=
2
b
c
=
2
b
(
a
−
b
)
=
2
a
b
−
2
b
2
\begin{aligned}a^2-(b^2+c^2)&=(b+c)^2-(b^2+c^2)\\&=2bc\\&=2b(a-b)\\&=2ab-2b^2\end{aligned}
a2−(b2+c2)=(b+c)2−(b2+c2)=2bc=2b(a−b)=2ab−2b2
设
f
(
b
)
=
2
a
b
−
2
b
2
f(b)=2ab-2b^2
f(b)=2ab−2b2
则
f
′
(
b
)
=
2
a
−
4
b
f^{'}(b)=2a-4b
f′(b)=2a−4b
可得在
b
=
a
2
b=\cfrac{a}{2}
b=2a的时候取到极大值。
同理,将
a
a
a尽可能平分得到的平方和减少的会最多。
所以我们设
g
(
a
,
b
)
g(a,b)
g(a,b)是将
a
a
a分成
b
b
b份的时候的平方和。
我们只需要按照
g
(
a
,
b
)
−
g
(
a
,
b
+
1
)
g(a,b)-g(a,b+1)
g(a,b)−g(a,b+1)的值维护优先队列即可。
ps.
这题思路对了,结果优先队列不会写重载,现场学了一下结果写的时候写错了一个小地方。然后这场血崩,直接掉蓝。
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define int long long
#define cl(x,y) memset(x,y,sizeof(x))
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define all(x) x.begin(),x.end()
#define lson x<<1,l,mid
#define rson x<<1|1,mid+1,r
#define INF 1e18
const int N=1e6+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const double eps=1e-8;
const double pi=acos(-1);
using namespace std;
struct edge
{
int w,t,id;
}a[N];
int f(int w,int t)
{
int res=0;
int w1=w%t,w2=t-w1,v=w/t;
res+=w1*(v+1)*(v+1);
res+=w2*v*v;
return res;
}
struct cmp
{
bool operator ()(const edge& a, const edge& b)
{
int sum1=f(a.w,a.t),sum2=f(a.w,a.t+1);
int sum3=f(b.w,b.t),sum4=f(b.w,b.t+1);
return sum1-sum2<sum3-sum4;
}
};
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,k,i;
cin>>n>>k;
int p=k-n;
priority_queue<edge,vector<edge>,cmp> q;
for(i=1;i<=n;i++)
{
cin>>a[i].w;
a[i].t=1;
a[i].id=i;
if(a[i].t==a[i].w)
continue;
q.push(a[i]);
}
while(p--)
{
edge pre=q.top();
q.pop();
a[pre.id].t++;
if(a[pre.id].t==a[pre.id].w)
continue;
q.push(a[pre.id]);
}
int ans=0;
for(i=1;i<=n;i++)
ans+=f(a[i].w,a[i].t);
cout<<ans<<endl;
return 0;
}