Luogu 3332 [ZJOI2013]K大数查询

BZOJ 3110

很早就想写的试炼场题。

不会整体二分啊呜呜呜,只能写写树套树。

有一个trick就是外层使用一个权值线段树,把位置作为下标的线段树放在内层,这样子的话我们在查询$k$大的时候就可以直接在外层线段树上一边走一边二分。

注意我们查询的是第$k$大不是第$k$小,所以我们在走的时候要观察右子树的权值和$k$的关系。

内层可以动态开点防mle,注意在打标记下传的时候要先给左右儿子赋值。

挺卡常的。

时间复杂度$O(nlog^2n)$。

Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N = 5e4 + 5;

int n, qn, mn, tot = 0;
ll val[N];

struct Querys {
    int type, l, r;
    ll k;
} q[N];

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > '9'|| ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

namespace SegT {
    struct Node {
        int lc, rc, tag;
        ll sum;
    } s[N * 400];
    
    int root[N << 2], nodeCnt = 0;
    
    #define lc(p) s[p].lc
    #define rc(p) s[p].rc
    #define sum(p) s[p].sum
    #define tag(p) s[p].tag
    #define mid ((l + r) >> 1)
    
    inline void up(int p) {
        if(!p) return;
        sum(p) = sum(lc(p)) + sum(rc(p));
    }
    
    inline void down(int p, int l, int r) {
        if(!tag(p)) return;
        if(!lc(p)) lc(p) = ++nodeCnt;
        if(!rc(p)) rc(p) = ++nodeCnt;
        sum(lc(p)) += 1LL * tag(p) * (mid - l + 1), tag(lc(p)) += tag(p);
        sum(rc(p)) += 1LL * tag(p) * (r - mid), tag(rc(p)) += tag(p);
        tag(p) = 0;
    }
    
    void ins(int &p, int l, int r, int x, int y) {
        if(!p) p = ++nodeCnt;
        if(x <= l && y >= r) {
            ++tag(p);
            sum(p) += 1LL * (r - l + 1);
            return;
        }
        
        down(p, l, r);
        if(x <= mid) ins(lc(p), l, mid, x, y);
        if(y > mid) ins(rc(p), mid + 1, r, x, y);
        up(p);
    }
    
    ll getSum(int p, int l, int r, int x, int y) {
        if(!p) return 0LL;
        if(x <= l && y >= r) return sum(p);
        
        down(p, l, r);
        
        ll res = 0LL;
        if(x <= mid) res += getSum(lc(p), l, mid, x, y);
        if(y > mid) res += getSum(rc(p), mid + 1, r, x, y);
        return res;
    }
    
    void modify(int p, int l, int r, int x, int y, int v) {
        ins(root[p], 1, n, x, y);
        if(l == r) return;
        
        if(v <= mid) modify(p << 1, l, mid, x, y, v);
        else modify(p << 1 | 1, mid + 1, r, x, y, v);
    }
    
    int query(int p, int l, int r, int x, int y, ll k) {
        if(l == r) return l;
        
        ll now = getSum(root[p << 1 | 1], 1, n, x, y);
        if(now >= k) return query(p << 1 | 1, mid + 1, r, x, y, k);
        else return query(p << 1, l, mid, x, y, k - now);
    }
    
} using namespace SegT;

int main() {
    read(n), read(qn);
    for(int i = 1; i <= qn; i++) {
        read(q[i].type), read(q[i].l), read(q[i].r), read(q[i].k);
        if(q[i].type == 1) val[++tot] = q[i].k;
    }
    
    sort(val + 1, val + tot + 1);
    tot = unique(val + 1, val + 1 + tot) - val - 1;
    mn = tot;
    
    for(int i = 1; i <= qn; i++) {
        if(q[i].type == 1) {
            int v = lower_bound(val + 1, val + 1 + tot, q[i].k) - val;
            modify(1, 1, mn, q[i].l, q[i].r, v);
        } else printf("%lld\n", val[query(1, 1, mn, q[i].l, q[i].r, q[i].k)]);
    }
    
    return 0;
}
View Code

 

posted @ 2018-09-29 20:52  CzxingcHen  阅读(192)  评论(0编辑  收藏  举报