zoukankan      html  css  js  c++  java
  • [BZOJ 3110] K大数查询

    Link:https://www.lydsy.com/JudgeOnline/problem.php?id=3110

    Solution:

    一道树套树的题,外层是权值线段树,里层是区间线段树


    对于权值线段树的节点 u 表示权值区间 [l, r),其对应的区间线段树的节点 v 表示序列 [l1, r1)中一共有多少个在权值区间 [l, r) 的数。


    查询:要查 [a, b]的第 k 大,只要每次判断是向右子节点还是左子节点走即可

    如果权值为[l,mid]中[a,b]区间的个数大于k,则说明在[l,mid]中,否则在[mid+1,r]中


    修改:使用标记永久化,尽量不用PushDown


    剩下的问题就是空间,理论上需要 O(n^2)的空间,我们可以对区间线段树动态开点

    好像整体二分也能做?待填

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    
    const int MAXN=5e4+10,MAXM=MAXN*20*20;
    ll sum[MAXM],lazy[MAXM];
    int n,m,a,b,c,cnt,root[MAXN*4],lc[MAXM],rc[MAXM];
    
    ll Query(int cur,int l,int r)
    {
        if (a<=l && r<=b) return sum[cur];
        ll t1=0,t2=0;int mid=(l+r)>>1;
        if (a<=mid) t1=Query(lc[cur],l,mid);
        if (mid<b)  t2=Query(rc[cur],mid+1,r);
        return (t1+t2)+1ll*(min(r,b)-max(a,l)+1)*lazy[cur];
    }
    
    int query()
    {
        int l=1,r=n,cur=1;
        while(l!=r)
        {
            int mid=(l+r)/2;
            ll t=0;
            if ((t=Query(root[cur<<1],1,n))>=c) r=mid,cur<<=1;
            else l=mid+1,cur=(cur<<1)+1,c-=t;
        }
        return l;
    }
    
    void modify(int &cur,int l,int r)
    {
        if (!cur) cur=++cnt;
        if (a<=l && r<=b)
        {
            sum[cur]+=r-l+1;
            lazy[cur]++;
            return;
        }
        int mid=(l+r)/2;
        if (a<=mid) modify(lc[cur],l,mid);
        if (mid<b) modify(rc[cur],mid + 1,r);
        sum[cur]=sum[lc[cur]]+sum[rc[cur]]+lazy[cur]*(r-l+1);
    }
    
    void update()
    {
        int l=1,r=n,cur=1;
        while(l!=r)
        {
            int mid=(l+r)/2;
            modify(root[cur],1,n);
            if (mid<c) l=mid+1,cur=(cur<<1)+1;
            else r=mid,cur<<=1;
        }
        modify(root[cur],1,n);
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        while (m--) 
        {
            int op;
            scanf("%d%d%d%d",&op,&a,&b,&c);
            if (op==1) c=n-c+1,update();
            else printf("%d
    ",n-query()+1);
        }
    }

    Review:

    1、可以通过c=n+1-c的方式将  求第K大   ----->   求第K小

    2、注意开long long,此题会爆int

    3、能用int尽量用int,long long在32位机上的计算是int的几倍耗时

         OI使用32位机

  • 相关阅读:
    window 7系统环境同时安装window xp系统,形成双系统
    工作感悟
    数据湖框架选型很纠结?一文了解Apache Hudi核心优势
    mysql数据库设计-规则
    maven中多个子模块的构建顺序
    EXTJS3.0 表单元素TextField datefield 设置只读并改背景颜色为灰色
    MySQL5.7的账号回收权限
    哲学王子-复旦博导王德峰教授:阅读与哲学思考
    abseil 的 cmake 方式编译
    [javascript] ie下audio不支持一些媒体类型
  • 原文地址:https://www.cnblogs.com/newera/p/9087467.html
Copyright © 2011-2022 走看看