zoukankan      html  css  js  c++  java
  • BZOJ3110[Zjoi2013]K大数查询——权值线段树套线段树

    题目描述

    有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
    如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

    输入

    第一行N,M
    接下来M行,每行形如1 a b c或2 a b c

    输出

    输出每个询问的结果

    样例输入

    2 5
    1 1 2 1
    1 1 2 2
    2 1 1 2
    2 1 1 1
    2 1 2 3

    样例输出

    1
    2
    1

    提示

    【样例说明】

    第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1

    的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是

    1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3

    大的数是 1 。‍

    N,M<=50000,N,M<=50000

    a<=b<=N

    1操作中abs(c)<=N

    2操作中c<=Maxlongint

    显然一棵线段树或平衡树无法维护这道题的操作,那么考虑树套树。

    一开始可能会想将维护序列的线段树放在外层,然后将维护序列每个点各种权值的权值线段树放在内层。

    这样修改操作就是外层线段树区间修改,内层线段树单点修改,很好操作。

    但你发现无法完成询问操作,如果用二分答案来解决的话时间复杂度就是O(nlogn^3)的了。

    既然询问第k大,那么显然权值线段树对于这种操作更为擅长,我们将权值线段树放在外层,将维护序列的线段树放在内层。

    这样修改就变成了外层线段树单点修改,内层线段树区间修改,同样很好操作。

    对于询问,我们每查询到权值线段树的一个节点先在这个点左子节点的内层线段树上区间查找,即找出序列上询问区间中权值小于某个值的数量,再与k判断来决定往左走还是往右走。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<bitset>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int n,m;
    int opt;
    int cnt;
    int a,b;
    ll c;
    ll s[20000010];
    int root[2000010];
    ll sum[20000010];
    int ls[20000010];
    int rs[20000010];
    inline void pushup(int rt)
    {
        sum[rt]=sum[ls[rt]]+sum[rs[rt]];
    }
    inline void pushdown(int rt,int l,int r)
    {
        if(s[rt])
        {
            int mid=(l+r)>>1;
            if(!ls[rt])
            {
                ls[rt]=++cnt;
            }
            if(!rs[rt])
            {
                rs[rt]=++cnt;
            }
            s[ls[rt]]+=s[rt];
            s[rs[rt]]+=s[rt];
            sum[ls[rt]]+=s[rt]*(mid-l+1);
            sum[rs[rt]]+=s[rt]*(r-mid);
            s[rt]=0;
        }
    }
    inline void change(int &rt,int l,int r,int L,int R)
    {
        if(!rt)
        {
            rt=++cnt;
        }
        if(L<=l&&r<=R)
        {
            sum[rt]+=(r-l+1);
            s[rt]++;
            return ;
        }
        pushdown(rt,l,r);
        int mid=(l+r)>>1;
        if(L<=mid)
        {
            change(ls[rt],l,mid,L,R);
        }
        if(R>mid)
        {
            change(rs[rt],mid+1,r,L,R);
        }
        pushup(rt);
    }
    inline ll find(int rt,int l,int r,int L,int R)
    {
        if(!rt)
        {
            return 0;
        }
        if(L<=l&&r<=R)
        {
            return sum[rt];
        }
        pushdown(rt,l,r);
        int mid=(l+r)>>1;
        ll res=0;
        if(L<=mid)
        {
            res+=find(ls[rt],l,mid,L,R);
        }
        if(R>mid)
        {
            res+=find(rs[rt],mid+1,r,L,R);
        }
        return res;
    }
    inline void insert(int rt,int l,int r,int k,int L,int R)
    {
        change(root[rt],1,n,L,R);
        if(l==r)
        {
            return ;
        }
        int mid=(l+r)>>1;
        if(k<=mid)
        {
            insert(rt<<1,l,mid,k,L,R);
        }
        else
        {
            insert(rt<<1|1,mid+1,r,k,L,R);
        }
    }
    inline int query(int rt,int l,int r,ll k,int L,int R)
    {
        if(l==r)
        {
            return l;
        }
        int mid=(l+r)>>1;
        ll res=find(root[rt<<1|1],1,n,L,R);
        if(res<k)
        {
            return query(rt<<1,l,mid,k-res,L,R);
        }
        else
        {
            return query(rt<<1|1,mid+1,r,k,L,R);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&opt);
            scanf("%d%d%lld",&a,&b,&c);
            if(a>b)
            {
                swap(a,b);
            }
            if(opt==1)
            {
                c=(int)c;
                insert(1,0,2*n,c+n,a,b);
            }
            else
            {
                printf("%d
    ",query(1,0,2*n,c,a,b)-n);
            }
        }
    }
  • 相关阅读:
    【JavaWeb】MVC案例之新闻列表
    PayPal高级工程总监:读完这100篇论文 就能成大数据高手(附论文下载)
    自己动手搭建搜索工具
    某学院软件工程复试回忆总结
    【NLP】Tika 文本预处理:抽取各种格式文件内容
    OpenNLP:驾驭文本,分词那些事
    【类库】私房干货.Net数据层方法的封装
    Oracle手边常用70则脚本知识汇总
    Oracle手边常用命令及操作语句
    快速了解什么是自然语言处理
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10032020.html
Copyright © 2011-2022 走看看