[2017纪中10-22]清兰 贪心+优先队列

题目链接: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值