BZOJ1112 - [POI2008]砖块Klo

Posted visjiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ1112 - [POI2008]砖块Klo相关的知识,希望对你有一定的参考价值。

原题链接

题意简述

给出一个\(n(n \leq 10^5)\)个数的序列\(a(max\{a\}\leq10^6)\),每次给一个数+1/-1。求使得序列中存在连续\(k(k \leq n)\)个相等的数至少要操作几次。

分析

题目实际上求的是\(|x_1-h|+|x_2-h|+...+|x_k-h|\)的最小值,其中\(x\)\(a\)的一个长度为\(k\)的子串。
易知\(h\)为序列\(x\)的中位数时原式取得最小值,那我们的任务就是求区间中位数咯。

证明

记序列\(x\)中小于\(h\)的有\(c_1\)个,大于\(h\)的有\(c_2\)个。
因为当\(h\)增大\(\Delta h\),原式就增大\(\Delta h \times c_1 - \Delta h \times c_2\)
所以当\(c_1<c_2\)时,\(h\)越大原式越小;\(c_1>c_2\)时,\(h\)越大原式越大。
\(c_1=c_2\)时原式取得最小值,此时\(h\)为序列\(x\)的中位数。

因为\(max\{a\}\leq10^6\)可以开数组,我们用树状数组实现。
维护\(x\)中小于等于\(i\)的数的个数\(cnt[i]\),小于等于\(i\)的数的和\(sum[i]\)。首先通过二分找出中位数\(h_0\),则原式的最小值为\[h_0 \times cnt[i]-sum[i] + h_0 \times (k-cnt[i]) \times (\Sigma{x_i}-sum[i])\]
总时间复杂度约为\(O(nlog^2max\{a\})\)

实现

开两个树状数组分别维护\(cnt[i],sum[i]\)

代码

//[POI2008]鐮栧潡Klo
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long lint;
int const N=1e5+10;
int const M=1e6+10;
lint const INF=1LL<<62;
int n,k,a[N];
int maxH; lint s[N];
lint tr[M],trs[M];
void add(int x,int f)
{
    int x1=x;
    while(x1<=maxH) tr[x1]+=f,x1+=x1&(-x1);
    x1=x;
    while(x1<=maxH) trs[x1]+=f*x,x1+=x1&(-x1);
}
lint sum1(int x)
{
    lint res=0;
    while(x>0) res+=tr[x],x-=x&(-x);
    return res;
}
lint sum2(int x)
{
    lint res=0;
    while(x>0) res+=trs[x],x-=x&(-x);
    return res;
}
lint sol(int fr)
{
    int L=0,R=maxH;
    while(L<R)
    {
        int mid=(L+R)>>1;
        if(sum1(mid)<(k+1)/2) L=mid+1;
        else R=mid;
    }
    lint h=L,c1=sum1(h),c2=k-c1;
    lint s1=sum2(h),s2=s[fr+k-1]-s[fr-1]-s1;
    return (c1*h-s1)+(s2-c2*h);
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]++;
    for(int i=1;i<=n;i++) maxH=max(maxH,a[i]);
    for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    lint ans=INF;
    for(int i=1;i<=k;i++) add(a[i],1);
    ans=min(ans,sol(1));
    for(int i=2;i<=n-k+1;i++)
    {
        add(a[i-1],-1); add(a[i+k-1],1);
        ans=min(ans,sol(i));
    }
    printf("%lld",ans);
    return 0;
}

注意

要开long long
原数列中可能有0,先给全体+1s+1

以上是关于BZOJ1112 - [POI2008]砖块Klo的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1112 POI2008 砖块Klo

[BZOJ1112][POI2008]砖块Klo

BZOJ1112: [POI2008]砖块Klo

BZOJ1112: [POI2008]砖块Klo

BZOJ1112 - [POI2008]砖块Klo

bzoj1112[POI2008]砖块Klo Treap