• [POI2008]KLO-Building blocks


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

    1,把这段区间变成几

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

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

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

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

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

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

    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
    ",ans);
        for(int i=1;i<=n;i++)
            if(p<i+k&&p>=i)
                printf("%lld
    ",ave);
            else
                printf("%lld
    ",v[i]);
        return 0;
    }
  • 相关阅读:
    C# 小算法1
    函数 y=x^x的分析
    随机数
    对拍
    Cube Stack
    Permutation
    一笔画问题
    康托展开&&康托逆展开
    待完成
    小错误 17/8/10
  • 原文地址:https://www.cnblogs.com/ivanovcraft/p/9670242.html
Copyright © 2020-2023  润新知