[POI2008]砖块Klo(set维护中位数)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvmaooi/article/details/82051659

[POI2008]砖块Klo

Description

N 柱砖,希望有连续 K 柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务.

Input

第一行给出 N , K . ( 1 k n 100000 ) , 下面 N 行,每行代表这柱砖的高度. 0 h i 1000000

Output

最小的动作次数

Sample Input

5 3
3
9
2
3
1

Sample Output

2

解:

看题目发现一个性质,学过初中数学的同学都知道,肯定是把所有数变成中位数最优,所以这道题就变成了动态维护中位数,我们知道了中位数,然后每个作差即可。
如何优雅地维护中位数?
可以使用splay来做,听说权值线段树也可以做。
在网上看到一个简单的维护方法:set,不用手写splay啦!!!
在线维护中位数,比中位数大的放到一个set里,比中位数小的放到另一个set里。如果两个set大小不同就调整一下。这样就很简单地完成啦!

似乎要特判一下n=1.
code(话说set还是挺好写的):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;

int n,k,a[100005],mid;
long long num1,num2,ans=0x7f7f7f7f7f7f7f;
multiset <int> d1,d2;
multiset <int>::iterator t;
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
      scanf("%d",&a[i]);
    mid=a[1];d2.insert(a[1]);num2+=a[1];
    for(int i=2;i<=n;i++){
        if(i>k){
            if(a[i-k]>=mid) d2.erase(d2.find(a[i-k])),num2-=a[i-k];
            else d1.erase(d1.find(a[i-k])),num1-=a[i-k];
        }
        if(a[i]>=mid) d2.insert(a[i]),num2+=a[i];
        else d1.insert(a[i]),num1+=a[i];
        while(d1.size()+1<d2.size()){
            t=d2.begin();
            num2-=*t;num1+=*t;
            d1.insert(*t);
            d2.erase(t);
        }
        while(d1.size()>d2.size()){
            t=d1.end();t--;
            num1-=*t;num2+=*t;
            d2.insert(*t);
            d1.erase(t);
        }
        mid=*d2.begin();
        if(i>=k){
          long long r=0;
          r+=num2-1ll*d2.size()*mid;
          r+=1ll*d1.size()*mid-num1;
          ans=min(r,ans);
        }
    }
    if(n==1) ans=0;
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/lvmaooi/article/details/82051659