[POI2008]磚塊Klo(set維護中位數)
阿新 • • 發佈:2018-11-07
[POI2008]磚塊Klo
Description
柱磚,希望有連續 柱的高度是一樣的. 你可以選擇以下兩個動作 1:從某柱磚的頂端拿一塊磚出來,丟掉不要了. 2:從倉庫中拿出一塊磚,放到另一柱.倉庫無限大. 現在希望用最小次數的動作完成任務.
Input
第一行給出 , . , 下面 行,每行代表這柱磚的高度.
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);
}