POJ 2104:K-th Number(整体二分)

http://poj.org/problem?id=2104

题意:给出n个数和m个询问求区间第K小。

思路:以前用主席树做过,这次学整体二分来做。整体二分在yr大佬的指点下,终于大概懂了点了。对于二分能够解决的询问,如果有多个,那么如果支持离线处理的话,那么就可以使用整体二分了。

在这题二分可行的答案,根据这个答案,把询问操作丢在左右两个队列里面分别递归继续按这样处理。注释里写的很详细。

 1 #include <iostream>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cmath>
 6 #include <algorithm>
 7 #include <ctime>
 8 #include <vector>
 9 #include <queue>
10 #include <map>
11 #include <set>
12 using namespace std;
13 typedef long long LL;
14 const int N = 150011;
15 const int INF = 0x3f3f3f3f;
16 struct P {
17     int type, l, r, val, id;
18 } q[N], lq[N], rq[N];
19 int ans[N], bit[N], sum[N], gap;
20 
21 int lowbit(int x) { return x & (-x); }
22 void update(int x, int w) { while(x <= gap) bit[x] += w, x += lowbit(x); }
23 int query(int x) { int ans = 0; while(x) ans += bit[x], x -= lowbit(x); return ans; }
24 
25 void Solve(int lask, int rask, int l, int r) { // 整体二分答案,判断答案是否可行
26     if(rask < lask || r < l) return ;
27     if(l == r) // 如果找到合适的答案了,那么在这段区间的查询的全部答案都是属于它
28         { for(int i = lask; i <= rask; i++) if(q[i].type == 2) ans[q[i].id] = l; return ; }
29     int mid = (l + r) >> 1, lcnt = 0, rcnt = 0;
30     for(int i = lask; i <= rask; i++) { // 遍历这段询问
31         if(q[i].type == 1) { // 如果是插入
32             if(q[i].val <= mid) { // 因为查询的是第k小,那么就是升序排列的第k个数字
33                 lq[++lcnt] = q[i]; // 如果插入的数比当前枚举的答案小,那么符合条件,在树状数组上插入并丢入左队列
34                 update(q[i].id, 1);
35             } else rq[++rcnt] = q[i]; // 插入的数比当前枚举的答案大,对当前枚举的答案无影响,丢入右队列
36         } else { // 如果是询问
37             int num = query(q[i].r) - query(q[i].l - 1); // 那么查询这段区间有多少个数
38             if(q[i].val <= num) lq[++lcnt] = q[i]; // 如果这段区间的数字比当前查询的k多,那么说明答案在左边
39             else { // 否则在右边,在右边的话还要减去左边拥有的
40                 q[i].val -= num;
41                 rq[++rcnt] = q[i];
42             }
43         }
44     }
45     for(int i = lask; i <= rask; i++) if(q[i].type == 1 && q[i].val <= mid) update(q[i].id, -1);
46     for(int i = 1; i <= lcnt; i++) q[i+lask-1] = lq[i]; // 重新构造
47     for(int i = 1; i <= rcnt; i++) q[i+lask+lcnt-1] = rq[i];
48     Solve(lask, lask + lcnt - 1, l, mid); // 分两边递归处理
49     Solve(lask + lcnt, rask, mid + 1, r);
50 }
51 
52 int main() {
53     int n, m, a, b, c;
54     scanf("%d%d", &n, &m); gap = n;
55     for(int i = 1; i <= n; i++) {
56         scanf("%d", &a);
57         q[i] = (P){1, 1, 1, a, i};
58     }
59     for(int i = 1; i <= m; i++) {
60         scanf("%d%d%d", &a, &b, &c);
61         q[n+i] = (P){2, a, b, c, i};
62     }
63     Solve(1, n + m, -INF, INF);
64     for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);
65     return 0;
66 }

 

转载于:https://www.cnblogs.com/fightfordream/p/6286065.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值