线段树 || BZOJ 1112: [POI2008]砖块Klo
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1112
题解:
希望有连续K柱的高度是一样的,就先把1~K的数扔进线段树(线段树的下标就是数值,不需要离散化),求一波中位数和答案作为初始答案,
再从第K+1到N扫一遍,依次把每个数扔进线段树同时把第i-K个树弄出来扔掉,不断求中位数和更新答案就好了。
这里求序列中所有数到中位数的距离是这样求的:线段树多维护一个sum,当前序列中小于中位数的数的个数记为cnt1,
和为sum1,大于中位数的数的个数记为cnt2,和为sum2。于是答案就很显然是:pt(即中位数)*cnt1-sum1+sum2-pt*cnt2。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define ll long long 5 #define min(a,b) ((a)<(b)?(a):(b)) 6 using namespace std; 7 const int maxn=100000+50,maxh=1000000+50,max_h=maxh-40; 8 int N,K,Z; 9 ll sum[2],H[maxn],ans,pt,cnt[2]; 10 inline ll rd(){ 11 ll x=0;int f=1;char c=getchar(); 12 while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} 13 while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} 14 return f*x; 15 } 16 struct Tree{ 17 int l,r; 18 ll sum,cnt; 19 }t[maxh<<2]; 20 inline void Build(int x,int l,int r){ 21 t[x].l=l;t[x].r=r;int mid=(l+r)>>1; 22 if(l==r)return; 23 Build(x<<1,l,mid);Build(x<<1|1,mid+1,r); 24 return; 25 } 26 inline void Update(int x,int q,int o){ 27 int l=t[x].l,r=t[x].r,mid=(l+r)>>1; 28 if(l==r&&l==q){ 29 if(o==1){t[x].sum+=l; t[x].cnt++;} 30 else{t[x].sum-=l; t[x].cnt--;} 31 return; 32 } 33 int ls=x<<1,rs=x<<1|1; 34 if(q<=mid)Update(ls,q,o);else Update(rs,q,o); 35 t[x].sum=t[ls].sum+t[rs].sum; 36 t[x].cnt=t[ls].cnt+t[rs].cnt; 37 return; 38 } 39 inline int Find(int x,int z){//寻找中位数 40 int l=t[x].l,r=t[x].r,ls=x<<1,rs=x<<1|1; 41 if(l==r)return l; 42 if(t[ls].cnt>=z)return Find(ls,z);else return Find(rs,z-t[ls].cnt); 43 } 44 inline void Work(int x,int ql,int qr,int o){ 45 int l=t[x].l,r=t[x].r,mid=(l+r)>>1,ls=x<<1,rs=x<<1|1; 46 if(ql<=l&&r<=qr){ 47 sum[o]+=t[x].sum; 48 cnt[o]+=t[x].cnt; 49 return; 50 } 51 if(ql<=mid)Work(ls,ql,qr,o); 52 if(qr>mid)Work(rs,ql,qr,o); 53 return; 54 } 55 int main(){ 56 N=rd();K=rd(); 57 for(int i=1;i<=N;i++){H[i]=rd();H[i]++;} 58 Build(1,1,max_h); 59 for(int i=1;i<=K;i++)Update(1,H[i],1); 60 Z=(K+1)>>1; 61 pt=Find(1,Z);//pt即为中位数 62 sum[0]=sum[1]=cnt[0]=cnt[1]=0; 63 Work(1,1,pt,0);Work(1,pt,max_h,1); 64 ans=(cnt[0]*pt-sum[0])+(sum[1]-cnt[1]*pt); 65 for(int i=K+1;i<=N;i++){ 66 Update(1,H[i-K],2);Update(1,H[i],1); 67 pt=Find(1,Z); 68 sum[0]=sum[1]=cnt[0]=cnt[1]=0; 69 Work(1,1,pt,0);Work(1,pt,max_h,1); 70 if(ans>(cnt[0]*pt-sum[0])+(sum[1]-cnt[1]*pt)){ 71 ans=(cnt[0]*pt-sum[0])+(sum[1]-cnt[1]*pt); 72 } 73 } 74 printf("%lld\n",ans); 75 return 0; 76 }
By:AlenaNuna