BZOJ 3110:[Zjoi2013]K大数查询(整体二分)

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

题意:……

思路:其实和之前POJ那道题差不多,只不过是换成区间更新,而且是第k大不是第k小,第k大是降序的第k个,在二分询问的时候需要注意和第k小的不同细节。

树状数组比线段树快了几倍,所以说树状数组区间更新区间查询是一个值得学的姿势啊。

线段树:

 1 //9028 kb    7484 ms
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 #define N 50010
 7 #define lson rt<<1, l, m
 8 #define rson rt<<1|1, m + 1, r
 9 typedef long long LL;
10 struct P {
11     int type, l, r, id; LL val;
12     P () {}
13     P (int type, int l, int r, LL val, int id) : type(type), l(l), r(r), val(val), id(id) {}
14 } q[N], lq[N], rq[N], qq[N];
15 LL tree[N<<2], lazy[N<<2], ans[N];
16 int n;
17
18 void pushup(int rt) { tree[rt] = tree[rt<<1|1] + tree[rt<<1]; }
19
20 void pushdown(int rt, int len) {
21     if(lazy[rt]) {
22         lazy[rt<<1] += lazy[rt]; lazy[rt<<1|1] += lazy[rt];
23         tree[rt<<1] += lazy[rt] * (len - (len >> 1)); tree[rt<<1|1] += lazy[rt] * (len >> 1);
24         lazy[rt] = 0;
25     }
26 }
27
28 void update(int rt, int l, int r, int L, int R, int val) {
29     if(L <= l && r <= R) {
30         tree[rt] += (r - l + 1) * val; // 居然漏了+1
31         lazy[rt] += val; return ;
32     }
33     pushdown(rt, r - l + 1);
34     int m = (l + r) >> 1;
35     if(L <= m) update(lson, L, R, val);
36     if(m < R) update(rson, L, R, val);
37     pushup(rt);
38 }
39
40 LL query(int rt, int l, int r, int L, int R) {
41     if(L <= l && r <= R) return tree[rt];
42     pushdown(rt, r - l + 1);
43     int m = (l + r) >> 1;
44     LL ans = 0;
45     if(L <= m) ans += query(lson, L, R);
46     if(m < R) ans += query(rson, L, R);
47     return ans;
48 }
49
50 void Solve(int l, int r, int lask, int rask) {
51     if(rask < lask || r < l) return ;
52     if(l == r) { for(int i = lask; i <= rask; i++) if(q[i].type == 2) ans[q[i].id] = l; return ; }
53     int mid = (l + r) >> 1, lcnt = 0, rcnt = 0;
54     for(int i = lask; i <= rask; i++) {
55         if(q[i].type == 1) {
56             if(mid >= q[i].val) { // 第k大 = 降序第k个,只更新比当前二分的答案大的
57                 lq[++lcnt] = q[i];
58             } else {
59                 rq[++rcnt] = q[i];
60                 update(1, 1, n, q[i].l, q[i].r, 1);
61             }
62         } else {
63             LL num = query(1, 1, n, q[i].l, q[i].r);
64             if(num >= q[i].val) rq[++rcnt] = q[i];
65             else {
66                 q[i].val -= num;
67                 lq[++lcnt] = q[i];
68             }
69         }
70     }
71     for(int i = lask; i <= rask; i++) if(q[i].type == 1 && mid < q[i].val) update(1, 1, n, q[i].l, q[i].r, -1);
72     for(int i = 1; i <= lcnt; i++) q[i+lask-1] = lq[i];
73     for(int i = 1; i <= rcnt; i++) q[i+lask+lcnt-1] = rq[i];
74     Solve(l, mid, lask, lask + lcnt - 1);
75     Solve(mid + 1, r, lask + lcnt, rask);
76 }
77
78 int main() {
79     int m, a, b, c, d, ins = 0, ask = 0;
80     scanf("%d%d", &n, &m);
81     for(int i = 1; i <= m; i++) {
82         scanf("%d%d%d%d", &a, &b, &c, &d);
83         if(a == 1) q[i] = P(a, b, c, d, ++ins);
84         else q[i] = P(a, b, c, d, ++ask);
85     }
86     Solve(1, n, 1, m);
87     for(int i = 1; i <= ask; i++)
88         printf("%lld\n", ans[i]);
89     return 0;
90 }

树状数组:

 1 //1828ms    6.7MB
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 #define N 50010
 7 #define lson rt<<1, l, m
 8 #define rson rt<<1|1, m + 1, r
 9 typedef long long LL;
10 struct P {
11     int type, l, r, id; LL val;
12     P () {}
13     P (int type, int l, int r, LL val, int id) : type(type), l(l), r(r), val(val), id(id) {}
14 } q[N], lq[N], rq[N], qq[N];
15 LL bit[2][N], ans[N];
16 int n;
17
18 int lowbit(int x) { return x & (-x); }
19 void Add(int i, int x, int w) { while(x <= n) bit[i][x] += w, x += lowbit(x); }
20 LL Sum(int i, int x) { LL ans = 0; while(x) ans += bit[i][x], x -= lowbit(x); return ans; }
21 void update(int l, int r, int w) {
22     Add(0, l, w); Add(0, r + 1, -w); Add(1, l, w * l); Add(1, r + 1, -w * (r + 1));
23 }
24 LL query(int l, int r) {
25     LL lsum = Sum(0, l - 1) * l - Sum(1, l - 1);
26     LL rsum = Sum(0, r) * (r + 1) - Sum(1, r);
27     return rsum - lsum;
28 }
29
30 void Solve(int l, int r, int lask, int rask) {
31     if(rask < lask || r < l) return ;
32     if(l == r) { for(int i = lask; i <= rask; i++) if(q[i].type == 2) ans[q[i].id] = l; return ; }
33     int mid = (l + r) >> 1, lcnt = 0, rcnt = 0;
34     for(int i = lask; i <= rask; i++) {
35         if(q[i].type == 1) {
36             if(mid >= q[i].val) { // 第k大 = 降序第k个,只更新比当前二分的答案大的
37                 lq[++lcnt] = q[i];
38             } else {
39                 rq[++rcnt] = q[i];
40                 update(q[i].l, q[i].r, 1);
41             }
42         } else {
43             LL num = query(q[i].l, q[i].r);
44             if(num >= q[i].val) rq[++rcnt] = q[i];
45             else {
46                 q[i].val -= num;
47                 lq[++lcnt] = q[i];
48             }
49         }
50     }
51     for(int i = lask; i <= rask; i++) if(q[i].type == 1 && mid < q[i].val) update(q[i].l, q[i].r, -1);
52     for(int i = 1; i <= lcnt; i++) q[i+lask-1] = lq[i];
53     for(int i = 1; i <= rcnt; i++) q[i+lask+lcnt-1] = rq[i];
54     Solve(l, mid, lask, lask + lcnt - 1);
55     Solve(mid + 1, r, lask + lcnt, rask);
56 }
57
58 int main() {
59     int m, a, b, c, d, ins = 0, ask = 0;
60     scanf("%d%d", &n, &m);
61     for(int i = 1; i <= m; i++) {
62         scanf("%d%d%d%d", &a, &b, &c, &d);
63         if(a == 1) q[i] = P(a, b, c, d, ++ins);
64         else q[i] = P(a, b, c, d, ++ask);
65     }
66     Solve(1, n, 1, m);
67     for(int i = 1; i <= ask; i++)
68         printf("%lld\n", ans[i]);
69     return 0;
70 }
时间: 2024-10-13 02:55:24

BZOJ 3110:[Zjoi2013]K大数查询(整体二分)的相关文章

BZOJ 3110: [Zjoi2013]K大数查询 [整体二分]

有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. N,M<=50000,N,M<=50000a<=b<=N1操作中abs(c)<=N2操作中c<=Maxlongint 之前用树套树抄过一次...然而我并不适合写那玩意儿... 加上时间序的整体二分 普通的整体二分先处理了所有$[l,mid]$的影响因子在计算询问的答案来分组

BZOJ 3110 [Zjoi2013]K大数查询 ——整体二分

[题目分析] 整体二分显而易见. 自己YY了一下用树状数组区间修改,区间查询的操作. 又因为一个字母调了一下午. 貌似树状数组并不需要清空,可以用一个指针来维护,可以少一个log 懒得写了. [代码] #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 50005 #define inf

树套树专题——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

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

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

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

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

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

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

题目链接: BZOJ - 3110 题目分析 这道题是一道树套树的典型题目,我们使用线段树套线段树,一层是区间线段树,一层是权值线段树.一般的思路是外层用区间线段树,内层用权值线段树,但是这样貌似会很难写.多数题解都使用了外层权值线段树,内层区间线段树,于是我就这样写了.每次插入会在 logn 棵线段树中一共建 log^2(n) 个结点,所以空间应该开到 O(nlog^2(n)) .由于这道题查询的是区间第 k 大,所以我们存在线段树中的数值是输入数值的相反数(再加上 n 使其为正数),这样查第

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

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

【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 题解 整体二分+树状数组区间修改 当年naive的树套树题解 前两天由于要

[BZOJ 3110][Zjoi2013]K大数查询(整体二分+BIT)

Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Solution 标解似乎是树套树?=w= 二分答案. 对于每一个修改,如果c>mid就进行操作,并划到后一个集合里,反之加入前一个集合:对于每一个询问,如果a-b中的数大于c个就划到后一个集合里,反之要减掉a-b中数的个数并加入前一个集合.然后对于两个集合递归下去blahb