zoukankan      html  css  js  c++  java
  • 【BZOJ4552】排序(TJOI&HEOI2016)-二分答案+线段树

    测试地址:排序
    做法:本题需要用到二分答案+线段树。
    看到在序列上做一些花里胡哨的操作,我们就想到用线段树,但是……这种操作好像根本做不了啊……
    这时我们发现了此题的一个重要条件:询问只有一次,并且在所有修改操作之后。也就是说,我们也许可以找到一个方法进行离线询问。
    这时候我们要转化问题。考虑最后的序列,我们把整个序列看做是按照数字从大到小一个一个插入到对应位置中去的,那么我们要求的位置上第一次被放进去数时,这个数就是答案。我们发现这个位置上有没有数这个东西是单调的,即在某一个时刻之前都没有数,而在之后都有数,所以我们考虑二分答案来找出这个时刻,那么就转化成了判定性问题:如何判断在进行m次排序操作后,某一个位置上有没有一个x的数?
    我们把所有数分成两类,一类是<x的,一类是x的,我们知道第二类数一定大于第一类数,所以我们用01分别表示这两类数,问题就转化成维护一个01序列,支持排序操作。对01序列排序可以分为三步:询问区间中0的个数,修改一个连续段内的数字为0,修改另一个连续段内的数字为1。显然这些操作都可以用线段树来维护。那么我们就解决了这个问题,时间复杂度为O(nlog2n)
    (这道题出的真的贼好,吹爆出题人)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,q,x,a[100010],op[100010],L[100010],R[100010];
    int seg[400010],p[400010];
    
    void pushdown(int no,int l,int r)
    {
        int mid=(l+r)>>1;
        if (p[no]!=-1)
        {
            p[no<<1]=p[no<<1|1]=p[no];
            if (!p[no])
            {
                seg[no<<1]=mid-l+1;
                seg[no<<1|1]=r-mid;
            }
            else seg[no<<1]=seg[no<<1|1]=0;
            p[no]=-1;
        }
    }
    
    void pushup(int no)
    {
        seg[no]=seg[no<<1]+seg[no<<1|1];
    }
    
    void buildtree(int no,int l,int r)
    {
        p[no]=-1;
        if (l==r)
        {
            if (a[l]>=x) seg[no]=0;
            else seg[no]=1;
            return;
        }
        int mid=(l+r)>>1;
        buildtree(no<<1,l,mid);
        buildtree(no<<1|1,mid+1,r);
        pushup(no);
    }
    
    void modify(int no,int l,int r,int s,int t,int type)
    {
        if (s>t) return;
        if (l>=s&&r<=t)
        {
            p[no]=type;
            if (!type) seg[no]=r-l+1;
            else seg[no]=0;
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no,l,r);
        if (s<=mid) modify(no<<1,l,mid,s,t,type);
        if (t>mid) modify(no<<1|1,mid+1,r,s,t,type);
        pushup(no);
    }
    
    int query(int no,int l,int r,int s,int t)
    {
        if (l>=s&&r<=t) return seg[no];
        int mid=(l+r)>>1,sum=0;
        pushdown(no,l,r);
        if (s<=mid) sum+=query(no<<1,l,mid,s,t);
        if (t>mid) sum+=query(no<<1|1,mid+1,r,s,t);
        return sum;
    }
    
    bool check()
    {
        buildtree(1,1,n);
        for(int i=1;i<=m;i++)
        {
            int tot;
            tot=query(1,1,n,L[i],R[i]);
            if (!op[i])
            {
                modify(1,1,n,L[i],L[i]+tot-1,0);
                modify(1,1,n,L[i]+tot,R[i],1);
            }
            else
            {
                modify(1,1,n,R[i]-tot+1,R[i],0);
                modify(1,1,n,L[i],R[i]-tot,1);
            }
        }
        return !query(1,1,n,q,q);
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&op[i],&L[i],&R[i]);
        scanf("%d",&q);
    
        int l=1,r=n;
        while(l<r)
        {
            x=((l+r)>>1)+1;
            if (check()) l=x;
            else r=x-1;
        }
        printf("%d",l);
    
        return 0;
    }
  • 相关阅读:
    32位和64位的区别
    Git--版本管理的使用及理解
    Maven使用详解
    记录centos7下tomcat部署war包过程
    SSM三大框架整合教程
    Mybatis 框架搭建实例
    Eclipse 出现select type (? = any character,*= any String,Tz=TimeZone)
    JDBC 操作数据库实例
    mysql 常用命令语法
    MySQL下载安装详情教程(Windows)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793414.html
Copyright © 2011-2022 走看看