P3332 [ZJOI2013]K大数查询

\(\color{#0066ff}{ 题目描述 }\)

有N个位置,M个操作。操作有两种,每次操作如果是:

  • 1 a b c:表示在第a个位置到第b个位置,每个位置加上一个数c
  • 2 a b c:表示询问从第a个位置到第b个位置,第C大的数是多少。

\(\color{#0066ff}{输入格式}\)

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

\(\color{#0066ff}{输出格式}\)

输出每个询问的结果

\(\color{#0066ff}{输入样例}\)

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

\(\color{#0066ff}{输出样例}\)

1
2
1

\(\color{#0066ff}{数据范围与提示}\)

第一个操作后位置 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<=long long

\(\color{#0066ff}{题解}\)

这里是区间加上一个数,可以用树套树做qwq

个人认为整体二分比较好写,而且用一个线段树维护区间加,区间求和就行了

注意开long long!!!!

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
struct Tree {
protected:
    struct node {
        node *ch[2];
        int l, r;
        LL val, tag;
        node(int l = 0, int r = 0, LL val = 0, LL tag = 0): l(l), r(r), val(val), tag(tag) { ch[0] = ch[1] = NULL; }
        void upd() { val = ch[0]->val + ch[1]->val; }
        void trn(LL k) { val += 1LL * (r - l + 1) * k, tag += k; }
        int mid() { return (l + r) >> 1; }
        void dwn() {
            if(!tag) return;
            ch[0]->trn(tag), ch[1]->trn(tag),
            tag = 0;
        }
    }*root;
    void build(node *&o, int l, int r) {
        o = new node(l, r, 0, 0);
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(o->ch[0], l, mid);
        build(o->ch[1], mid + 1, r);
    }
    void lazy(node *o, int l, int r, LL k) {
        if(l <= o->l && o->r <= r) return o->trn(k);
        o->dwn();
        if(l <= o->mid()) lazy(o->ch[0], l, r, k);
        if(r > o->mid()) lazy(o->ch[1], l, r, k);
        o->upd();
    }
    LL query(node *o, int l, int r) {
        if(l <= o->l && o->r <= r) return o->val;
        o->dwn();
        LL ans = 0;
        if(l <= o->mid()) ans += query(o->ch[0], l, r);
        if(r > o->mid()) ans += query(o->ch[1], l, r);
        return ans;
    }
public:
    void build(int l, int r) { build(root, l, r); }
    void lazy(int l, int r, LL k) { lazy(root, l, r, k); }
    LL query(int l, int r) { return query(root, l, r); }
}s;
const int maxn = 1e5 + 10;
struct node {
    int tp, l, r, id;
    LL k;
    node(int tp = 0, int l = 0, int r = 0, int id = 0, LL k = 0): tp(tp), l(l), r(r), id(id), k(k) {}
}q[maxn], ql[maxn], qr[maxn];
int n, m, num, id[maxn];
LL ans[maxn];
void work(LL l, LL r, int nl, int nr) {
    if(l > r || nl > nr) return;
    if(l == r) {
        for(int i = nl; i <= nr; i++) if(q[i].tp == 2) ans[q[i].id] = l;
        return;
    }
    LL mid = (l + r) >> 1, cntl = 0, cntr = 0;
    for(int i = nl; i <= nr; i++) {
        if(q[i].tp == 2) {
            LL k = s.query(q[i].l, q[i].r);
            if(q[i].k <= k) qr[++cntr] = q[i];  //这里应该放右边!!
            else q[i].k -= k, ql[++cntl] = q[i];
        }
        else {
            if(q[i].k <= mid) ql[++cntl] = q[i];
            else qr[++cntr] = q[i], s.lazy(q[i].l, q[i].r, 1);
        }
    }
    for(int i = nl; i <= nr; i++) if(q[i].tp == 1 && q[i].k > mid) s.lazy(q[i].l, q[i].r, -1);
    for(int i = 1; i <= cntl; i++) q[nl + i - 1] = ql[i];
    for(int i = 1; i <= cntr; i++) q[nl + cntl + i - 1] = qr[i];
    work(l, mid, nl, nl + cntl - 1), work(mid + 1, r, nl + cntl, nr);
}
int main() {
    n = in(), m = in();
    s.build(1, n);
    int p, x, y;
    LL max = 0, z;
    for(int i = 1; i <= m; i++) {
        p = in(), x = in(), y = in(), z = in();
        q[i] = node(p, x, y, i, z);
        max = std::max(max, z);
        if(p == 2) id[++num] = i;
    }
    work(-max, max, 1, m);
    for(int i = 1; i <= num; i++) printf("%lld\n", ans[id[i]]);
    return 0;
}

原文地址:https://www.cnblogs.com/olinr/p/10408614.html

时间: 2024-10-29 02:37:00

P3332 [ZJOI2013]K大数查询的相关文章

洛谷 P3332 [ZJOI2013]K大数查询 || bzoj3110

用树套树就很麻烦,用整体二分就成了裸题.... 错误: 1.尝试线段树套平衡树,码农,而且n*log^3(n)慢慢卡反正我觉得卡不过去 2.线段树pushdown写错...加法tag对于区间和的更新应该要乘上区间长度的 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 typedef long long LL; 6 struct Q 7 { 8 LL

P3332 [ZJOI2013]K大数查询 整体二分

终于入门整体二分了,勉勉强强算是搞懂了一个题目吧. 整体二分很多时候可以比较好的离线处理区间\(K\)大值的相关问题.考虑算法流程: 操作队列\(arr\),其中有询问和修改两类操作. 每次在答案的可行值域上二分一个\(mid\),把询问的答案\(>mid\)的分在\(R\)部,\(<=mid\)的分在\(L\)部.把修改的值\(>mid\)的分在\(R\)部,\(<=mid\)的分在\(L\)部. 何谓整体二分?就是直接一起二分所有的询问操作的答案,然后暴力扫描当前操作区间,将其

BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )

BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ----------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std;

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

【BZOJ3110】[Zjoi2013]K大数查询 树套树

[BZOJ3110][Zjoi2013]K大数查询 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c,如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3

树套树专题——bzoj 3110: [Zjoi2013] K大数查询 &amp; 3236 [Ahoi2013] 作业 题解

[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 978  Solved: 476 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Outpu

[ZJOI2013]K大数查询——整体二分

新科技:整体二分 它能解决的典型问题:带修改区间第\(k\)大 大概的做法是这样的:我们一次二分一个值\(mid\),然后依据操作的答案与\(mid\)的大小关系把操作分别划到两边,然后递归下去.也就是相当于二分的是所有询问的答案 感觉其实这个跟在权值线段树上二分一个效果,只是用离线的方式替代掉了那一层权值线段树而已 计算可得复杂度为\(O(nlog^2n)\)(由主定理,\(T(n)=2T(n/2)+O(nlogn)=O(nlog^2n)\)) 拿线段树或者树状数组维护都行 板子题是这一道K大

bzoj3110: [Zjoi2013]K大数查询 【树套树,标记永久化】

好久没写题解了. 但是这题太神了然后做法太神了于是写一下. 这题做法很多,比如黄学长hzw的权值线段树套线段树,比如学长云的bit套主席树(其实是写法更神然后我不会用). 然后看到hzhwcmhf大神题解. http://tieba.baidu.com/p/2246783535 震惊了. 好了开说说做法.建一颗朴素的线段树,树的每个点表示每个区间,然后每个区间建两棵树,一棵是mark树,一棵是all树,两棵都是权值线段树. “mark表示该区间每个点上都会加上mark线段树里的元素 all表示该