• BZOJ 1112 [POI2008]砖块Klo(可持久化线段树)


    【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=1112

    【题目大意】

      给出一个数列,对于一个操作,你可以对一个数+1,或者一个数-1,
      问若使得数列中出现长度为m的连续相同的数,最少需要的操作数。

    【题解】

      我们发现对于固定区间求最小操作,等价于求区间中数距离中位数差值和最小,
      我们发现区间中位数可以利用主席树求区间kth来实现,
      同时在主席树上维护权值线段树的区间真值和,那么对于每个区间中的数,
      就能分别维护比中位数小的部分的和以及比中位数大的部分的和,
      分别与中位数的倍数做加减运算即可。
      我们枚举区间的位置,保留最小值,复杂度O(nlogn)。

    【代码】

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N=100010;
    typedef long long LL;
    int n,m,i,x,y,z,ans;
    int l[N*40],r[N*40],v[N*40],tot,root[N],num[N];
    LL s[N*40];
    int cmp(int i,int j){return num[i]<num[j];}
    int rk[N],sa[N];  //sa:该数字对应原数列的下标 
    int build(int a,int b){
        int x=++tot; v[x]=s[x]=0;
        if(a==b)return x;
        int mid=(a+b)>>1;
        return l[x]=build(a,mid),r[x]=build(mid+1,b),x;
    }
    // x版本c位置+p,在s中记录+num[sa[c]],统计权值线段树上真值的区间和。
    int change(int x,int a,int b,int c,int p){
        int y=++tot;v[y]=v[x]+p;s[y]=s[x]+num[sa[c]];
        if(a==b)return y;
        int mid=(a+b)>>1;
        if(c<=mid)l[y]=change(l[x],a,mid,c,p),r[y]=r[x];
        else l[y]=l[x],r[y]=change(r[x],mid+1,b,c,p);
        return y;
    }
    int kth(int x,int y,int a,int b,int k){
        if(a==b)return a;
        int mid=(a+b)>>1;
        if(v[l[y]]-v[l[x]]>=k)return kth(l[x],l[y],a,mid,k);
        else return kth(r[x],r[y],mid+1,b,k-v[l[y]]+v[l[x]]);
    }
    LL abs(LL x){return x<0?-x:x;}
    LL query(int x,int y,int a,int b,int c){
        if(a==b)return 1LL*abs(num[sa[a]]-num[sa[c]])*(v[y]-v[x]);
        int mid=(a+b)>>1;
        if(c<=mid){
            return query(l[x],l[y],a,mid,c)+s[r[y]]-s[r[x]]-1LL*num[sa[c]]*(v[r[y]]-v[r[x]]);
        }else{
            return query(r[x],r[y],mid+1,b,c)+1LL*num[sa[c]]*(v[l[y]]-v[l[x]])-(s[l[y]]-s[l[x]]);
        }
    } 
    int main(){
        while(~scanf("%d%d",&n,&m)){
            root[0]=0;tot=0; 
            for(int i=1;i<=n;i++)scanf("%d",num+i);  
            for(int i=1;i<=n;i++)sa[i]=i;  
            sort(sa+1,sa+n+1,cmp); 
            for(int i=1;i<=n;i++)rk[sa[i]]=i;
            build(1,n);
            for(int i=1;i<=n;i++)root[i]=change(root[i-1],1,n,rk[i],1);
            LL ans=0x3f3f3f3f3f3f3f3f;
            for(int i=1;i<=n-m+1;i++){
                int id=kth(root[i-1],root[i+m-1],1,n,(m+1)>>1);
                ans=min(ans,query(root[i-1],root[i+m-1],1,n,id));
            }printf("%lld
    ",ans);
        }return 0;
    }
  • 相关阅读:
    已开启博客园~
    友链
    javacv 接收RTSP流(或avi/mp4视频文件),输出byte[]
    Springboot项目中,使用logback来管理日志。
    PPT文件流转为图片,并压缩成ZIP文件输出到指定目录
    通过AOP自定义注解实现记录用户操作日志。
    使用javacv,解码socket接收的H264码流(byte[]),转为yuv处理,最后再合成转为H264
    idea 开始java之旅
    浅谈Winform控件开发(一):使用GDI+美化基础窗口
    WinformGDI+入门级实例——扫雷游戏(附源码)
  • 原文地址:https://www.cnblogs.com/forever97/p/bzoj1112.html
Copyright © 2020-2023  润新知