Link.
Description.
有 \(n\) 个长度分别为 \(a_i\) 的线段。
你需要把它们划分成 \(k\) 个长度是整数的线段。
最小化最后 \(k\) 个线段的长度的平方和。
Solution.
如果我们一只一个长度为 \(L\) 的线段要划分成 \(K\) 段,那我们就已经能完全知道它的权值了。
然后毛咕咕一下或者证一下,我们发现在 \(K\) 递增时它单调(废话),且是凸的。
发现 \(K\) 很小,直接用一个堆来维护当前每段线段 \(K\) 加一时权值会减少多少。
直接贪即可。
Coding.
点击查看代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
struct node{ll w;int i,k;char operator<(node b) const {return w<b.w;}};
int n,K,a[100005];ll rs=0;priority_queue<node>q;inline ll sq(ll x) {return x*x;}
inline ll F(int i,int w) {return (w-a[i]%w)*sq(a[i]/w)+a[i]%w*sq(a[i]/w+1);}
int main()
{
read(n,K);for(int i=1;i<=n;i++) read(a[i]),rs+=F(i,1);
for(int i=1;i<=n;i++) q.push((node){F(i,1)-F(i,2),i,1});
for(int i=1;i<=K-n;i++)
{
node x=q.top();q.pop(),rs-=x.w,x.k++;
x.w=F(x.i,x.k)-F(x.i,x.k+1),q.push(x);
}return printf("%lld\n",rs),0;
}