zoukankan      html  css  js  c++  java
  • [洛谷P3332] [ZJOI2013]K大数查询

    洛谷题目链接:[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

    输出格式:

    输出每个询问的结果

    输入输出样例

    输入样例#1:

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

    输出样例#1:

    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
    a<=b<=N

    1操作中abs(c )<=N

    2操作中c<=long long

    题意这么简单就不多赘述了

    题解: 首先简单的树套树是不行的,因为这里涉及到了区间插入,而树套树不支持区间插入.就算是在外层树上打标记,涉及到的操作总数还是(O(N^2))级别的.

    据说这题如果要用树套树做可以交换权值和位置两个维度,然后再将区间插入某个权值改成某个权值插入多个位置,然后实现区间查询?反正我不太会.

    其实这题可以用整体二分的思想来做.

    首先二分一个权值,然后将所有小于该权值的插入操作都用线段树实现.查找某个区间的第(k)小也用线段树区间查询来实现,也就是查询这个区间内的和,因为只有比(mid)小的权值被加入了线段树中,所以如果线段树中的和小于这个询问的(k)的话,就需要有更多权值加入这个区间,也就是说要满足这个询问,二分的权值就要加大,否则就要减小.

    同时我们需要将这个修改的状态对答案的贡献都记录下来,避免重复统计.

    这道题是整体二分入门的一道好题,如果没看懂可以多想想,其实大多数博客写的内容都差不多,主要还是靠自己思考.

    #include<bits/stdc++.h>
    #define ll(x) (x<<1)
    #define rr(x) (x<<1|1)
    using namespace std;
    const int N =50000+5;
    
    int n, m, id[N], ans[N], t1[N], t2[N];
    
    struct segment_tree{
        int l, r, size;
        long long sum, tag;
    }t[N*4];
    
    struct options{
        int opt, l, r, k;
    }o[N];
    
    inline int gi(){
        int ans = 0, f = 1; char i = getchar();
        while(i<'0' || i>'9'){ if(i == '-') f = -1; i = getchar(); }
        while(i>='0' && i<='9') ans = ans*10+i-'0', i = getchar();
        return ans * f;
    }
    
    void build(int x, int l, int r){
        t[x].l = l, t[x].r = r, t[x].size = r-l+1, t[x].tag = 0;
        if(l == r) return; int mid = (l+r>>1);
        build(ll(x), l, mid), build(rr(x), mid+1, r);
    }
    
    inline void up(int x){ t[x].sum = t[ll(x)].sum+t[rr(x)].sum; }
    
    inline void pushdown(int x){
        if(!t[x].tag) return;
        t[ll(x)].tag += t[x].tag, t[ll(x)].sum += t[x].tag*t[ll(x)].size;
        t[rr(x)].tag += t[x].tag, t[rr(x)].sum += t[x].tag*t[rr(x)].size;
        t[x].tag = 0;
    }
    
    void update(int x, int l, int r, long long val){
        if(l <= t[x].l && t[x].r <= r){
            t[x].tag += val;
            t[x].sum += val*t[x].size;
            return;
        }
        int mid = (t[x].l+t[x].r>>1); pushdown(x);
        if(l <= mid) update(ll(x), l, r, val);
        if(mid < r) update(rr(x), l, r, val); up(x);
    }
    
    long long query(int x, int l, int r){
        if(l <= t[x].l && t[x].r <= r) return t[x].sum;
        if(t[x].r < l || r < t[x].l) return 0;
        pushdown(x);
        return query(ll(x), l, r)+query(rr(x), l, r);
    }
    
    void solve(int l, int r, int ql, int qr){
        if(ql > qr) return;
        if(l == r){
            for(int i=ql;i<=qr;i++)
                if(o[id[i]].opt == 2) ans[id[i]] = l;
            return;
        }
        int mid = (l+r>>1), cnt1 = 0, cnt2 = 0, len = ql-1;
        long long sum;
        for(int i=ql;i<=qr;i++){
            if(o[id[i]].opt == 1){
                if(o[id[i]].k > mid) t1[++cnt1] = id[i], update(1, o[id[i]].l, o[id[i]].r, 1);
                else t2[++cnt2] = id[i];
            } else {
                sum = query(1, o[id[i]].l, o[id[i]].r);
                if(sum >= o[id[i]].k) t1[++cnt1] = id[i];
                else t2[++cnt2] = id[i], o[id[i]].k -= sum;
            }
        }
        for(int i=ql;i<=qr;i++)
            if(o[id[i]].opt == 1 && o[id[i]].k > mid)
                update(1, o[id[i]].l, o[id[i]].r, -1);
        for(int i=1;i<=cnt2;i++) id[++len] = t2[i];
        for(int i=1;i<=cnt1;i++) id[++len] = t1[i];
        solve(l, mid, ql, ql+cnt2-1), solve(mid+1, r, ql+cnt2, qr);
    }
    
    int main(){
        //freopen("data.in", "r", stdin);
        n = gi(), m = gi(), build(1, 1, n);
        for(int i=1;i<=m;i++)
            o[i].opt = gi(), o[i].l = gi(), o[i].r = gi(), o[i].k = gi(), id[i] = i;
        solve(-1e9, 1e18, 1, m);
        for(int i=1;i<=m;i++)
    	if(o[i].opt == 2) printf("%d
    ", ans[i]);
        return 0;
    }
    
  • 相关阅读:
    python之闭包,装饰器
    python之函数名称空间,作用域,嵌套函数
    python之函数基础
    Python之文件操作
    Linux之系统优化配置
    VMware安装CentOS操作系统详细步骤
    拷贝、浅拷贝、深拷贝解答
    python之字符串,列表,字典,元组,集合内置方法总结
    东方超环(EAST)世界纪录
    Vue通信、传值的多种方式,详解(都是干货)
  • 原文地址:https://www.cnblogs.com/BCOI/p/9599174.html
Copyright © 2011-2022 走看看