[POI2008]砖块Klo

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

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

Output
最小的动作次数

Sample Input
5 3
3
9
2
3
1

Sample Output
2

首先我简单讲一下题意,因为自己被坑过……
有n个柱子,每次你可以给一个柱子高度+1或-1,要用最少的步数使得有k个连续的柱子一样高

首先我们考虑k段,让这k个柱子到什么高度最优?对,中位数。

所以我们弄一个可以维护区间中位数的数据结构来,记得要把前面小于中位数的和后面大于中位数的和求出来,统计答案要用

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)     print(x/10);
    putchar(x%10+'0');
}
const int N=1e5;
ll Ans=1e18;
int V,L,R;
struct Splay{
    #define ls(x) tree[x][0]
    #define rs(x) tree[x][1]
    #define T(x) (rs(f[x])==x)
    int tree[N+10][2],f[N+10],size[N+10],v[N+10],root,len;
    ll sum[N+10];
    void clear(int x){size[x]=ls(x)=rs(x)=f[x]=sum[x]=v[x]=0;}
    void updata(int x){
        size[x]=size[ls(x)]+size[rs(x)]+1;
        sum[x]=sum[ls(x)]+sum[rs(x)]+v[x];
    }
    void move(int x){
        int fa=f[x],son=tree[x][T(x)^1];
        tree[x][T(x)^1]=fa;
        tree[fa][T(x)]=son;
        if (son)    f[son]=fa;
        f[x]=f[fa];
        if (f[x])   tree[f[x]][T(fa)]=x;
        f[fa]=x;
        updata(fa),updata(x);
    }
    void splay(int x){
        while (f[x]){
            if (f[f[x]])    T(x)==T(f[x])?move(f[x]):move(x);
            move(x);
        }
        root=x;
    }
    void insert(int x){
        v[++len]=x;
        if (!root){
            size[root=len]=1;
            return;
        }
        int i=root;
        while (true){
            size[i]++,sum[i]+=x;
            if (x<=v[i]){
                if (!ls(i)){f[ls(i)=len]=i;break;}
                i=ls(i);
            }else{
                if (!rs(i)){f[rs(i)=len]=i;break;}
                i=rs(i);
            }
        }
        splay(len);
    }
    int get_pre(){
        int x=ls(root);
        while (rs(x))   x=rs(x);
        return x;
    }
    int get_suc(){
        int x=rs(root);
        while (ls(x))   x=ls(x);
        return x;
    }
    int find(int i,int x){
        if (!i) return 0;
        if (x==size[ls(i)]+1)   return i;
        if (x<=size[ls(i)]) return find(ls(i),x);
        return find(rs(i),x-size[ls(i)]-1);
    }
    bool work(int k){
        splay(find(root,(k+1)>>1));
        ll res=size[ls(root)]*v[root]-sum[ls(root)]+sum[rs(root)]-size[rs(root)]*v[root];
        if (Ans>res){
            Ans=res,V=v[root];
            return 1;
        }
        return 0;
    }
    void Delete(int x){
        splay(x);
        if (!(ls(x)&&rs(x))){
            f[root=ls(x)+rs(x)]=0;
            clear(x);
            return;
        }
        int i=get_pre();
        splay(i);
        f[rs(i)=rs(x)]=i;
        clear(x);
        updata(root);
    }
    int get_rank(int x){
        int res=0;
        for (int i=root;i;){
            if (x<=v[i])    i=ls(i);
            else    res+=size[ls(i)]+1,i=rs(i);
        }
        return res+1;
    }
    void Del(int x){Delete(find(root,get_rank(x)));}
}T;
int val[N+10];
int main(){
    int n=read(),k=read();
    for (int i=1;i<=n;i++)  val[i]=read();
    for (int i=1;i<=k;i++)  T.insert(val[i]);
    for (int i=k+1;i<=n;i++){
        if (T.work(k))  L=i-k,R=i-1;
        T.Del(val[i-k]);
        T.insert(val[i]);
    }
    if (T.work(k))  L=n-k+1,R=n;
    printf("%lld\n",Ans);
//  for (int i=1;i<=n;i++)  printf("%d\n",(L<=i&&i<=R)?V:val[i]);
//原题要输出最后的状态,bzoj不用,故选择性注释掉
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Wolfycz/p/8954854.html