bzoj1112 [POI2008] 砖块Klo

题意:给你一个长为n的非负整数序列,每次可以付出1的代价使某个数字+1/-1,用最小的代价使得存在连续k个数数值相同,求最小代价。

对于一个长为k的区间,代价最小显然是所有数最后等于原先的中位数(区间长度为偶数时有多种选择),那么中位数和代价都可以主席树裸上。然后我WA了好多次.....

因为非负整数包括0,而我建的是1到1000000的权值线段树....整体+1就A了....

#include<cstdio>
const int maxn=100005;
struct node{
  node *ch[2];int sum;long long sum2;
  node(){}
  node(int x,long long _sum2){
    ch[0]=ch[1]=0;sum=x;sum2=_sum2;
  }
}t[maxn*100];int tot=0;
node* newnode(int x,long long s){
  t[++tot]=node(x,s);return t+tot;
}
node *root[maxn];
void Insert(node* rt0,node* &rt,int l,int r,int k){
  rt=newnode(rt0->sum+1,rt0->sum2+k);
  if(l==r)return;
  int mid=(l+r)>>1;
  if(k<=mid){
    Insert(rt0->ch[0],rt->ch[0],l,mid,k);rt->ch[1]=rt0->ch[1];
  }else{
    Insert(rt0->ch[1],rt->ch[1],mid+1,r,k);rt->ch[0]=rt0->ch[0];
  }
}
int kth(node* rtl,node* rtr,int l,int r,int k){
  if(l==r){return l;}
  int sum0=rtr->ch[0]->sum-rtl->ch[0]->sum,mid=(l+r)>>1;
  //printf("%d\n",sum0);
  if(k<=sum0)return kth(rtl->ch[0],rtr->ch[0],l,mid,k);
  else return kth(rtl->ch[1],rtr->ch[1],mid+1,r,k-sum0);
}
int Abs(int x){
  return x>=0?x:-x;
}
long long query(node* rtl,node* rtr,int l,int r,int lim){
  if(l==r)return Abs(l-lim)*1LL*(rtr->sum-rtl->sum);
  int mid=(l+r)>>1;
  if(lim<=mid){
    return query(rtl->ch[0],rtr->ch[0],l,mid,lim)+(rtr->ch[1]->sum2-rtl->ch[1]->sum2)-(lim*1LL*(rtr->ch[1]->sum-rtl->ch[1]->sum));
  }else{
    return query(rtl->ch[1],rtr->ch[1],mid+1,r,lim)-(rtr->ch[0]->sum2-rtl->ch[0]->sum2)+(lim*1LL*(rtr->ch[0]->sum-rtl->ch[0]->sum));
  }
}
int middle(int l,int r){
  int len=r-l+1;
  if(len&1)return kth(root[l-1],root[r],1,1000001,len/2+1);
  else return (kth(root[l-1],root[r],1,1000001,len/2+1)+kth(root[l-1],root[r],1,1000001,len/2))/2;
}
int main(){
  root[0]=newnode(0,0);root[0]->ch[0]=root[0]->ch[1]=t+tot;
  int n,k,x;scanf("%d%d",&n,&k);
  for(int i=1;i<=n;++i){
    scanf("%d",&x);++x;Insert(root[i-1],root[i],1,1000001,x);
  }
  long long ans=9223372036854775807LL;
  for(int i=1;i+k-1<=n;++i){
    long long tmp=query(root[i-1],root[i+k-1],1,1000001,middle(i,i+k-1));
    //printf("%d %lld\n",middle(i,i+k-1),tmp);
    if(tmp<ans)ans=tmp;
  }
  printf("%lld\n",ans);
  return 0;
}

 

posted @ 2017-01-24 21:41  liu_runda  阅读(572)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难