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

[POI2008]砖块Klo

Description

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

Input

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

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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是用set维护中位数的C程序: ```c #include <stdio.h> #include <stdlib.h> #include <set> using namespace std; int main() { multiset<int> nums; // 定义multiset容器 int n, x; scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &x); nums.insert(x); // 插入元素 if(i % 2 == 1) { // 奇数个元素时,取中间值 multiset<int>::iterator it = nums.begin(); // 获取迭代器 advance(it, i / 2); // 移动迭代器i/2个位置 printf("%d\n", *it); // 输出中位数 } else { // 偶数个元素时,取中间两个数的平均值 multiset<int>::iterator it1 = nums.begin(); // 获取迭代器 advance(it1, i / 2 - 1); // 移动迭代器 multiset<int>::iterator it2 = it1; it2++; // 移动迭代器 printf("%d\n", (*it1 + *it2) / 2); // 输出中位数 } } return 0; } ``` 程序的流程是这样的: 1. 定义一个multiset容器,用于存储输入的数。 2. 读入输入的数,将数插入multiset中。 3. 根据当前元素个数,计算中位数并输出。 对于奇数个元素的情况,直接找到中间位置的数即可。由于multiset中元素已经按从小到大的顺序排序,因此可以直接使用begin()函数获取第一个元素的迭代器,然后使用advance()函数移动迭代器i/2个位置即可得到中位数。 对于偶数个元素的情况,需要找到中间两个数的平均值。同样地,可以先找到第i/2-1个数和第i/2个数的迭代器,然后将它们的值相加,再除以2即可得到中位数。 最后,输出中位数即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值