【JZOJ5413】【NOIP2017提高A组集训10.22】清兰

Description

这里写图片描述

Data Constraint

对于30%的数据,满足n<=500,m<=200
对于70%的数据,满足n<=20000,m<=100000
对于100%的数据,满足
1<=n<=50000,1<=m<=100000000,-100<=L<=100,|si|<=100
对于每个部分,均有50%的数据满足L=0

Solution

我们发现对于一个间距若分成k个间隔,一定是均分更优。设 s[i]s[i1]=d,(dl)2=d2+l22dl

a[i]=d
l^2为定值,2*l* a[i]=2lda[i]2
由柯西不等式
ka[i]2>=(a[i])2=d2

我们发现分成k份后的贡献为 k(d/kl)2=d2/k+kl22dl 当k增加1时,答案的增量为 l2d2k(k+1) 这时我们就想到了70%的nlogn的做法,即每次从set中取出 l2d2k(k+1) 的最小值。
我们还可以证明,对于一个间隔,分在它的区间至少为 |s[i]s[i1]|m|s[i]s[i1]| 我们来证明一下:
|s[i]s[i1]|=sum
对于一个i,若存在一个最优值满足分于i的区间为 |s[i]s[i1]|m|s[i]s[i1]|1 ,另一个j的分于j的区间即为 |s[j]s[j1]|m|s[i]s[i1]|+1 ,那么我们现在若可以在i或j改变一个区间
di2(|di|msum1)(|di|msum)<=di2(|di|msum)2=sum2m2

dj2(|dj|msum+1)(|dj|msum+2)>=dj2(|dj|msum)2=sum2m2

明显增加i更优。
所以我们可以事先给每个区间 |s[i]s[i1]|msum 的区间,同时我们还可以由增量 l2d2k(k+1) 算出区间也至少为 l/d ,两者取min,然后继续执行70分算法。每个区间改变量不超过1。时间复杂度O(NlogN)。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define db double
#include<set>
#define ll long long
using namespace std;
const int maxn=5e4+5;
ll d[maxn];
ll n,m,i,l,j; 
db t,k,p,a[maxn],q,ans,x,y,z;
struct code{
    db x;
    int y,z;
    bool friend operator < (code x,code y){
        return x.x<y.x || x.x==y.x && x.y<y.y;
    }
}g;
set<code>f;
db sqr(db x){return x*x;}
int main(){
    freopen("seiran.in","r",stdin);freopen("seiran.out","w",stdout);
    scanf("%d%d%lf",&n,&m,&p);
    for (i=1;i<=n;i++)scanf("%lf",&a[i]),q+=abs(a[i]-a[i-1]);q-=abs(a[1]);
    for (i=2;i<=n;i++){
        x=abs(a[i]-a[i-1]);
        if (p)d[i]=min(x/abs(p),x/q*m);
        else d[i]=x/q*m;
        if (!d[i])d[i]++;
        ans+=d[i]*sqr((a[i]-a[i-1])/d[i]-p);g.z=2;
        if (p*p-x*x/((d[i]+1)*d[i])<0)g.x=p*p-x*x/((d[i]+1)*d[i]),g.z=1;
        else if (d[i]>1)g.x=-(p*p-x*x/((d[i]-1)*d[i])),g.z=-1;
        if (g.z!=2)g.y=i,f.insert(g);
    }
    for (i=2;i<=n;i++)
        m-=d[i]-1;
    while (m){
        g=*f.begin();
        if (g.x>=0) break;
        t++;
        ans+=g.x;m-=g.z;
        f.erase(g);x=a[g.y]-a[g.y-1];d[g.y]+=g.z;
        if (g.z<0 && d[g.y]==1) continue;
        g.x=g.z*(p*p-x*x/((d[g.y]+g.z)*d[g.y]));
        f.insert(g);
    }
    if (ans<0) ans=0;
    printf("%.3lf\n",ans);
}
发布了257 篇原创文章 · 获赞 451 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/crybymyself/article/details/78346838