题目链接:https://jzoj.net/senior/#main/show/5413
70pts:
使用柯西不等式可以证明均分一定是最优的。
假设两个数的差为 d,那么分为 k 份时对 f(x)的贡献是
分成 k+1 份时,增量为
发现随着 k 的增大,将 k 加一的改进值(增量的相反数)会越来越小,作用越来
越弱。因此,相当于有 n-1 个递减序列,取出不超过 m 个使得它们的和最大。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn=50010;
int n,m;
double s[50010],L,ans;
struct node
{
double d,k,t;
};
bool operator <(node a,node b) {return a.t<b.t;}
priority_queue <node ,vector<node> > Q;
int main()
{
scanf("%d%d%lf",&n,&m,&L);
for(int i=1;i<=n;i++)
scanf("%lf",&s[i]);
for(int i=1;i<=n-1;i++)
{
node c;
c.d=s[i+1]-s[i];c.k=2;c.t=(c.d*c.d/2-L*L);
Q.push(c);
ans+=(c.d-L)*(c.d-L);
}
for(int i=1;i<=m;i++)
{
node c=Q.top();Q.pop();
if(c.t<0) break;
ans-=c.t;c.k++;c.t=(c.d*c.d/(c.k-1)/c.k-L*L);
Q.push(c);
}
printf("%.3lf",ans);
return 0;
}
100pts:
所以先把每个间隔分成|di|*m/sumall份(注意不能大于|di|/L,否则越分越劣),可以证明之后再分的次数不多了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int maxn=50010;
int n,m;
double s[50010],L,ans,sum;
struct node
{
double d,k,t;
};
bool operator <(node a,node b) {return a.t<b.t;}
priority_queue <node ,vector<node> > Q;
int main()
{
scanf("%d%d%lf",&n,&m,&L);
for(int i=1;i<=n;i++)
scanf("%lf",&s[i]);
int rst=0;
for(int i=1;i<n;i++) sum+=abs(s[i+1]-s[i]);
for(int i=1;i<n;i++)
{
node c;c.d=s[i+1]-s[i];
int dk=min((int)(abs(c.d)*m/sum),(int)((L==0)?1e9:(abs(c.d/L))));
rst+=max(dk-1,0);
c.k=max(dk,1);c.t=(c.d*c.d/((c.k+1)*c.k)-L*L);
Q.push(c);
ans+=c.k*(c.d/c.k-L)*(c.d/c.k-L);
}
for(int i=1;i<=m-rst;i++)
{
node c=Q.top();Q.pop();
if(c.t<0) break;
ans-=c.t;c.k++;c.t=(c.d*c.d/((c.k+1)*c.k)-L*L);
Q.push(c);
}
printf("%.3lf",ans);
return 0;
}