码迷,mamicode.com
首页 > 其他好文 > 详细

[POI2008]KLO-Building blocks

时间:2018-09-18 19:21:34      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:can   return   枚举   kth   long   ext   i++   names   大于等于   

容易想到,我们可以枚举所有长度为$k$的区间,算出把这段区间全部变成一个值的代价,对于所有区间取最小值即可

1,把这段区间变成几

可能能够猜出变成中位数,别人都是显然,只有我证了证(太弱了),给出一个简单的证明

对于一个序列$S$,设它的中位数为x

设序列中有a个数小于x,b个数大于x,c个数等于x

设将这段区间变为x的代价为W

考虑把这段区间变为x+1,新代价为W+a+c-b,因为

a+c>=k/2\text{(中位数嘛)}

a+b+c=k

a+c>=b

,所以新代价大于等于原代价

变为x-1同理

考虑两个中位数不同,选哪个

设序列中有c个数等于较小中位数x,较大中位数为y

考虑上式,两个中位数不同,对于满足x+tmp<=y,a,b,c是不变的,a+c=b=k/2,新代价等于旧代价

这样,我们知道了不仅两个中位数代价相同,而且两个中位数之间所有数代价相同

2,具体求代价

序列和与中位数乘序列长度作差?

直接作差不行,对于大于等于中位数的数求和减去中位数乘它们的个数

对于小于中位数的数,中位数乘它们的个数减去它们的和,两项求和即可

选择一个支持这些操作(中位数,小于和,插入删除)的数据结构吧,我选择了主席树

然后就是暴力打码了,不解释了

#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=1e5+10,maxv=1e6+1;
struct tree{
    int v,ls,rs,sum;
}a[25*maxv];
int n,k,rt[maxn],cnt,v[maxn],ans=1e15,p,all,ave;
void insert(int pre,int cur,int p,int l,int r)
{
    if(l==r)
    {
        a[cur].v=a[pre].v+1;
        a[cur].sum=a[pre].sum+l;
        return;
    }
    int m=l+r>>1;
    if(p<=m)
    {
        a[cur].ls=++cnt;
        a[cur].rs=a[pre].rs;
        insert(a[pre].ls,a[cur].ls,p,l,m);
    }
    else
    {
        a[cur].rs=++cnt;
        a[cur].ls=a[pre].ls;
        insert(a[pre].rs,a[cur].rs,p,m+1,r);
    }
    a[cur].v=a[a[cur].ls].v+a[a[cur].rs].v;
    a[cur].sum=a[a[cur].ls].sum+a[a[cur].rs].sum;
}
int kth(int x,int y,int k,int l,int r)
{
    if(l==r)
        return l;
    int m=l+r>>1,num=a[a[y].ls].v-a[a[x].ls].v;
    if(num>=k)
        return kth(a[x].ls,a[y].ls,k,l,m);
    else
        return kth(a[x].rs,a[y].rs,k-num,m+1,r);
}
int sum(int x,int y,int k,int l,int r)
{
    if(l==r)
        return min(k,a[y].v-a[x].v)*l;
    int m=l+r>>1,num=a[a[y].ls].v-a[a[x].ls].v;
    if(num>=k)
        return sum(a[x].ls,a[y].ls,k,l,m);
    else
        return a[a[y].ls].sum-a[a[x].ls].sum+sum(a[x].rs,a[y].rs,k-num,m+1,r);
}
signed main()
{
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<k;i++)
    {
        scanf("%lld",&v[i]);
        rt[i]=++cnt,all+=v[i];
        insert(rt[i-1],rt[i],v[i],0,1e6);
    }
    for(int i=k;i<=n;i++)
    {
        scanf("%lld",&v[i]);
        rt[i]=++cnt,all+=v[i]-v[i-k];
        insert(rt[i-1],rt[i],v[i],0,1e6);
        int pos=k+1>>1,tmp=kth(rt[i-k],rt[i],pos,0,1e6),res=sum(rt[i-k],rt[i],pos,0,1e6);
        res=all-2*res-(k-pos)*tmp+pos*tmp;
        if(res<ans)
        {
            ans=res;
            p=i,ave=tmp;
        }
    }
    printf("%lld\n",ans);
    for(int i=1;i<=n;i++)
        if(p<i+k&&p>=i)
            printf("%lld\n",ave);
        else
            printf("%lld\n",v[i]);
    return 0;
}

[POI2008]KLO-Building blocks

标签:can   return   枚举   kth   long   ext   i++   names   大于等于   

原文地址:https://www.cnblogs.com/ivanovcraft/p/9670242.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!