【bzoj1112】【poi2008】【砖块】【treap】

来源:互联网 发布:财经杂志 知乎 编辑:程序博客网 时间:2024/04/28 23:41

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
题解:可以发现对于连续的k个,把它们都变成中位数最优.
所以用treap维护中位数即可.
因为要计算答案,所以treap里要维护权值和大小.
代码:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cstdlib>#define N 100010using namespace std;int n,m,a[N],cnt,K,root;long long t1,t2,ans(12345678987654321LL),mid;struct use{int l,r,rd,sz,num;long long sm,v;}tr[N];void update(int k){ tr[k].sz=tr[tr[k].l].sz+tr[tr[k].r].sz+tr[k].num; tr[k].sm=tr[tr[k].l].sm+tr[tr[k].r].sm+tr[k].v*tr[k].num;}void lturn(int &k){  int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k;  update(k);update(t);k=t;}void rturn(int &k){  int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;  update(k);update(t);k=t;}void insert(int &k,long long v){  if (!k){    k=++cnt;tr[k].v=tr[k].sm=v;tr[k].sz=tr[k].num=1;    tr[k].rd=rand();return;  }  //tr[k].sz++;tr[k].sm+=v;  if (tr[k].v==v) tr[k].num++;  else if (v<tr[k].v){    insert(tr[k].l,v);    if (tr[tr[k].l].rd<tr[k].rd) rturn(k);  }  else{    insert(tr[k].r,v);    if (tr[tr[k].r].rd<tr[k].rd) lturn(k);  }  update(k);}void del(int &k,long long v){  if (k==0) return;  if (tr[k].v==v){    if (tr[k].num>1){tr[k].sm-=v;tr[k].num--;tr[k].sz--;return;}    else{      if (tr[k].l*tr[k].r==0) k=tr[k].l+tr[k].r;      else if (tr[tr[k].l].rd<tr[k].rd){rturn(k);del(k,v);}      else{lturn(k);del(k,v);}    }  }  else if (v<tr[k].v){del(tr[k].l,v);}  else {del(tr[k].r,v);}  update(k);}void find(int k,int rk){  if (k==0) return;  if (tr[tr[k].l].sz>=rk){    t2+=tr[k].v*tr[k].num+tr[tr[k].r].sm;    find(tr[k].l,rk);  }  else if (tr[tr[k].l].sz+tr[k].num>=rk){    t1+=tr[tr[k].l].sm+(rk-tr[tr[k].l].sz-1)*tr[k].v;    t2+=tr[tr[k].r].sm+(tr[tr[k].l].sz+tr[k].num-rk)*tr[k].v;mid=tr[k].v;  }  else{  t1+=tr[tr[k].l].sm+tr[k].v*tr[k].num;  find(tr[k].r,rk-tr[tr[k].l].sz-tr[k].num);  }}void getans(){  t1=t2=0;int temp=(K+1)>>1;  find(root,temp);  long long sum=(temp-1)*mid-t1+t2-(K-temp)*mid;  ans=min(ans,sum); }int main(){  scanf("%d%d",&n,&K);  for (int i=1;i<=n;i++) scanf("%d",&a[i]);  for (int i=1;i<=K;i++) insert(root,(long long)a[i]);  getans();  for (int i=K+1;i<=n;i++){    del(root,a[i-K]);    insert(root,a[i]);getans();  }  cout<<ans<<endl;}



0 0
原创粉丝点击