[bzoj1112][树状数组]砖块Klo

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;
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Rose_max/article/details/79959154
个人分类: bzoj 树状数组
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭