题意:给你一个长为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 ",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 ",middle(i,i+k-1),tmp); if(tmp<ans)ans=tmp; } printf("%lld ",ans); return 0; }