zoukankan      html  css  js  c++  java
  • 【线段树】牛客 k小数查询 (区间线段树套权值线段树)

    K小数查询

    题意:####

    给一个长度为 n 数列 A ,然后m个操作,有如下两种:
    · 1 l r x,表示对i(in)[l,r],令 (A_i=min(A_i,x))
    · 2 l r k,表示询问区间[l,r]中第 k 小的数。

    输入描述:####

    第一行输入两个整数 (n,m(1≤n,m≤8×10^4))
    接下来一行 n 个整数描述数组 (A(1≤A_i≤n))
    接下来 m 行每行描述一个操作,操作格式与题面中相同,保证(1≤l≤r≤n,1≤k≤r−l+1,1≤x≤10^9)

    输出描述:####

    对于每组询问,输出一个整数表示答案。

    官方题解:####

    考虑区间线段树套权值线段树求区间k小值的算法:对 ([1,n]) 建线段树,每一个线段树节点 ([l,r]) ,用一棵动态开点的权值线段树记录 ([l,r]) 中每种权值出现了多少次。
    如果能够维护这样的数据结构,询问就可以转化为在 (O(logn)) 棵权值线段树上二分,能在 (O(lon^2n)) 的时间里得到答案。
    修改时,首先和普通的区间线段树一样,定位到某些节点[l,r]:这次修改相当于把内层线段树中所有大于等于 (x) 的数并到 (x) 的位置,这可以被转化成 (t+1) 次线段树单点修改操作,其中 (t) 为被并入的节点个数。把这 (t+1) 次修改应用到内层线段树 ([l,r]) 以及 ([l,r]) 所有祖先节点的内层线段树上。这一步的时间复杂度为 (O(tlog^2n))
    接着,在 ([l,r]) 上打上对 (x)(min) 的标记。在标记下传的时候,同样当前节点的内层线段树中,更大的节点的值并到 (x) 处,但是因为次数祖先节点的内层线段树的信息都是正确的,所以不需要把修改应用到祖先节点的内层线段树上。
    时间复杂度和所有操作的 (t) 的和有关,不难发现,每一次操作结束后,内层线段树的叶子节点个数都减少了 ,而最开始一共有 (O(nlogn)) 个叶子节点,所以 (t) 的总和不会超过 (O(nlogn)) 。因此总的时间复杂度为 (O(nlog^3n))



    个人:####

    对于动态开点建权值线段树 练习参考luogu3939以及luogu1383
    主要在于题解说 : 每一次操作结束后,内层线段树的叶子节点个数都减少了 ,而最开始一共有 (O(nlogn)) 个叶子节点,所以 (t) 的总和不会超过 (O(nlogn))。所以复杂度没有看起来那么高。
    比较难考虑到的是题解的up函数(就是上传内函数的修改)。
    ....好吧 整套思路还是难想 因为是二分答案+树套树... (加油(ง •_•)ง


    【看着题解又又又胡搞了份难看的代码,然后神奇的过了

    代码如下

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=3e7+5;
    const int inf=0x3f3f3f3f;
    
    int n;
    int T[maxn],L[maxn],R[maxn],sum[maxn],tot;
    int mi[maxn],up[maxn];
    //对每个区间 动态开点 建权值线段树
    inline void add(int&rt,int l,int r,int x,int v)
    {
        if(!rt)rt=++tot;
        if(l==r)
        {
            sum[rt]+=v;return;
        }
        int mid=(l+r)>>1;
        if(x<=mid)add(L[rt],l,mid,x,v);
        else add(R[rt],mid+1,r,x,v);
        sum[rt]=sum[L[rt]]+sum[R[rt]];
    }
    inline int get(int rt,int l,int r,int tl,int tr)
    {
        if(!rt)return 0;
        if(tl<=l&&r<=tr)return sum[rt];
        int mid=(l+r)>>1,res=0;
        if(tl<=mid)res+=get(L[rt],l,mid,tl,tr);
        if(tr>mid)res+=get(R[rt],mid+1,r,tl,tr);
        return res;
    }
    inline void build(int k,int l,int r,int x,int v)
    {
        mi[k]=inf;
        add(T[k],1,n,v,1);
        up[k]=max(up[k],v);
        if(l==r)return;
        int mid=(l+r)>>1;
        if(x<=mid)build(k<<1,l,mid,x,v);
        else build(k<<1|1,mid+1,r,x,v);
    }
    
    /*
     op==1: [l,r], Ai=min(Ai,x)
     等效把[l,r]区间里的大于x的数 并入x的计数,并清零
     */
    inline void down(int k)
    {
        int s;
        if(mi[k]<up[k<<1])
        {
            s=get(T[k<<1],1,n,mi[k]+1,up[k<<1]);
            add(T[k<<1],1,n,mi[k],s);
            mi[k<<1]=up[k<<1]=mi[k];
        }
        if(mi[k]<up[k<<1|1])
        {
            s=get(T[k<<1|1],1,n,mi[k]+1,up[k<<1|1]);
            add(T[k<<1|1],1,n,mi[k],s);
            up[k<<1|1]=mi[k<<1|1]=mi[k];
        }
        mi[k]=inf;
    }
    inline void upp(int k,int x,int num)
    {
        add(T[k],1,n,x,num);
        if(k>>1)upp(k>>1,x,num);
    }
    inline void clear(int rt,int k,int l,int r,int x,int num)
    {
        if(l==r)
        {
            sum[rt]-=num;
            if(k>>1)clear(T[k>>1],k>>1,1,n,x,num);
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid)clear(L[rt],k,l,mid,x,num);
        else clear(R[rt],k,mid+1,r,x,num);
        sum[rt]=sum[L[rt]]+sum[R[rt]];
    }
    inline void update(int k,int l,int r,int tl,int tr,int x)
    {
        if(x>=up[k])return ;
        if(tl<=l&&r<=tr)
        {
            int sum,num;
            sum=get(T[k],1,n,x+1,up[k]);
            if(sum)upp(k,x,sum);
            for(int i=x+1;i<=up[k]&&sum;i++)
            {
                num=get(T[k],1,n,i,i);sum-=num;
                if(num)clear(T[k],k,1,n,i,num);
            }
            up[k]=mi[k]=x;
            return;
        }
        if(mi[k]!=inf)down(k);
        int mid=(l+r)>>1;
        if(tl<=mid)update(k<<1,l,mid,tl,tr,x);
        if(tr>mid)update(k<<1|1,mid+1,r,tl,tr,x);
    }
    inline int query(int k,int l,int r,int tl,int tr,int x)
    {
        if(tl<=l&&r<=tr)
        {
            if(x>=up[k])return get(T[k],1,n,1,up[k]);
            else return get(T[k],1,n,1,x);
        }
        if(mi[k]!=inf)down(k);
        int mid=(l+r)>>1,res=0;
        if(tl<=mid)res+=query(k<<1,l,mid,tl,tr,x);
        if(tr>mid)res+=query(k<<1|1,mid+1,r,tl,tr,x);
        return res;
    }
    int main()
    {
        int m,x,op,l,r,L,R,mid,t,res;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            build(1,1,n,i,x);
        }
        while(m--)
        {
            scanf("%d%d%d%d",&op,&l,&r,&x);
            if(op==1)update(1,1,n,l,r,x);
            else
            {
                L=1,R=n,res=-1;
                while(L<=R)
                {
                    mid=(L+R)>>1;
                    t=query(1,1,n,l,r,mid);
    //                printf("#%d %d %d
    ",mid,t,x);
                    if(t>=x)
                    {
                        res=mid;
                        R=mid-1;
                    }
                    else L=mid+1;
                }
                printf("%d
    ",res);
            }
        }
    }
    



  • 相关阅读:
    Android Studio4.1.2中,修改了gradle后,如何在不关闭AS IDE的情况下使gradle进行sync
    Android-studio-ide-201.7042882-windows-4.1.2项目卡在Gradle: Download gradle-6.5-bin.zip
    Intellij IDEA开发环境中Springboot项目无Run ****main()的菜单
    《Bootstrap4Web设计与开发实战》源代码下载
    数字操作题目汇总
    机器学习模型跨平台上线
    koro1FileHeader 注释插件 vscode
    nginx https协议配置
    nginx 配置 不显示版本号
    Asp.Net Core 中的静态文件
  • 原文地址:https://www.cnblogs.com/kkkek/p/12244522.html
Copyright © 2011-2022 走看看