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
HINT
原题还要求输出结束状态时,每柱砖的高度.本题略去.
题解
你可以发现其实就是对于连续一段,把这一段放到数轴上。然后在数轴上找到一个点,使得这一段数到这个点的总距离最小
不难想到这个点其实就是中位数
于是我们可以枚举每一段,在树状数组上二分找到这个数
然后就没了。。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL a[1110000],mmax;
int s[1110000],n,K;
int lowbit(int x){return x&-x;}
void chs(int x,int c){while(x<=mmax){s[x]+=c;x+=lowbit(x);}}
void cha(int x,LL c){while(x<=mmax){a[x]+=c;x+=lowbit(x);}}
LL findsum(int x){LL ret=0;while(x>=1){ret+=a[x];x-=lowbit(x);}return ret;}
int findall(int x){int ret=0;while(x>=1){ret+=s[x];x-=lowbit(x);}return ret;}
LL h[1110000];
int main()
{
scanf("%d%d",&n,&K);
mmax=0;
for(int i=1;i<=n;i++)scanf("%lld",&h[i]),h[i]++,mmax=max(mmax,h[i]);
for(int i=1;i<K;i++)cha(h[i],h[i]),chs(h[i],1);
LL owb=1000000000000000000;
int mid=K/2+1;
for(int i=K;i<=n;i++)
{
cha(h[i],h[i]);chs(h[i],1);
if(i-K>=1)cha(h[i-K],-h[i-K]),chs(h[i-K],-1);
int cnt=0,ans=0;
for(int j=(1<<21);j;j>>=1)if(ans+s[cnt+j]<mid && cnt+j<=mmax)ans+=s[cnt+j],cnt+=j;
cnt++;
int sum=findall(cnt-1);
LL tmp=(LL)cnt*sum-findsum(cnt-1);
sum=findall(mmax)-findall(cnt);
tmp+=findsum(mmax)-findsum(cnt)-(LL)cnt*sum;
owb=min(owb,tmp);
}
printf("%lld\n",owb);
return 0;
}