BZOJ 1112: [POI2008]砖块Klo
Description
N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务.
Input
第一行给出N,K. (1 ≤ k ≤ n ≤ 100000), 下面N行,每行代表这柱砖的高度.0 ≤ hi ≤ 1000000
题解:
如果确定了将区间[l,r]变成一样的,那么变成中位数需要的操作次数最少。
可以用平衡树维护,支持查询第k大、查询小于k大数的和、查询大于k大数的和,利用这些可以快速的求出答案。
复杂度`O(nlogn)`。注意开LL。
代码:
(平衡树写的渣。)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> //by zrt //problem: using namespace std; typedef long long LL; const int inf(0x3f3f3f3f); const double eps(1e-9); LL n,k; LL a[100005]; LL num[100005],sum[100005],ls[100005],rs[100005],val[100005],fa[100005],siz[100005]; inline void upd(int o){ siz[o]=siz[ls[o]]+siz[rs[o]]+num[o]; sum[o]=num[o]*val[o]+sum[ls[o]]+sum[rs[o]]; } inline void zig(int x){ int y=fa[x]; if(rs[x]) ls[y]=rs[x],fa[rs[x]]=y; else ls[y]=0; fa[x]=fa[y]; if(fa[y]){ if(ls[fa[y]]==y) ls[fa[y]]=x;else rs[fa[y]]=x; } fa[y]=x;rs[x]=y; upd(y); } inline void zag(int x){ int y=fa[x]; if(ls[x]) rs[y]=ls[x],fa[ls[x]]=y; else rs[y]=0; fa[x]=fa[y]; if(fa[y]){ if(ls[fa[y]]==y) ls[fa[y]]=x;else rs[fa[y]]=x; } fa[y]=x;ls[x]=y; upd(y); } int root; inline void splay(int x,int z){ while(fa[x]!=z){ int y=fa[x]; if(fa[y]==z){ if(ls[y]==x) zig(x); else zag(x); }else{ if(ls[fa[y]]==y){ if(ls[y]==x){ zig(y),zig(x); }else{ zag(x);zig(x); } }else{ if(ls[y]==x){ zig(x),zag(x); }else{ zag(y),zag(x); } } } } if(!z) root=x; upd(x); } int c; inline void insert(LL x){ int o=root; int f=0; while(o){ f=o; if(val[o]==x){ num[o]++; splay(o,0); return; } if(x<val[o]){ o=ls[o]; }else{ o=rs[o]; } } o=++c; if(f){ if(x<val[f]){ ls[f]=o; }else{ rs[f]=o; } }else root=o; fa[o]=f; val[o]=x; num[o]=1; splay(o,0); } inline void del(LL x){ int o=root; while(1){ if(val[o]==x){ num[o]--; splay(o,0); return; } if(x<val[o]){ o=ls[o]; }else o=rs[o]; } } inline void ask(int k){ int o=root; while(1){ if(siz[ls[o]]>=k) { o=ls[o];continue; } if(siz[ls[o]]+num[o]>=k) { splay(o,0);return; }else{ k-=siz[ls[o]]+num[o]; o=rs[o]; } } } LL ans; int main(){ #ifdef LOCAL freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif ans=inf*1LL*1000000; scanf("%lld%lld",&n,&k); for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); } for(int i=1;i<k;i++){ insert(a[i]); } for(int i=k;i<=n;i++){ if(i>k){ del(a[i-k]); } insert(a[i]); ask((k+1)/2); LL aim=val[root]; ans=min(ans,siz[ls[root]]*aim-sum[ls[root]]+sum[rs[root]]-siz[rs[root]]*aim); } printf("%lld\n",ans); return 0; }