[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
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
2
1
HINT
【样例说明】
第一个操作 后位置 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<=Maxlongint
Source
Solution
树套树。。。说难不难,说简单不简单。曾经花了一星期理解内外线段树的关系。。。一直不理解怎么“套”一棵树的
树套树的一般做法是用两个树形结构,外层代表区间,内层代表该区间下的权值信息或其它的什么
相当于一个二维矩阵,一维是区间信息一维是权值信息。
嘛,其实就是外层线段树纪录它对应的内层线段树的节点编号,内层线段树纪录该区间下与某权值范围的的结点信息
为了省空间,需用动态开点的姿势。在这里给各位提个醒:这道题开2000000就好。
嗯= =其实就是把线段树update和query写两遍233
蛋碎了一地。数组开大MLE,数组开小RE。
这道题外层区间内层权值有点麻烦,所以换一种思路:外层权值线段树内层区间线段树。递归查询某一段权值范围内有多少数,若个数小于询问数则向左子树递归,否则向右子树递归。
= =
好吧我的确给别人讲不懂 _(:з」∠)_
代码常熟大地飞起。标记永久化是什么可以吃吗
yky大爷讲了一种奥妙丛丛的压缩空间的方法:把结构体里的变量压缩为x位整型。因为好像存和要用unsigned int于是干脆用long long。具体用法看代码。
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 struct seg1 5 { 6 ll l:28, r:28, sum:36, lazy:36; 7 }p1[20000005]; 8 struct operation 9 { 10 ll op, l, r, val; 11 }op[50005]; 12 ll n, p2[150005], ptot, cd[50005], ctot, ql, qr, val; 13 14 void push_up(ll o, ll l, ll r) 15 { 16 p1[o].sum = p1[p1[o].l].sum + p1[p1[o].r].sum + p1[o].lazy * (r - l + 1); 17 } 18 19 void push_down(ll o, ll l, ll r) 20 { 21 ll mid = (l + r) >> 1; 22 if(!p1[o].l) p1[o].l = ++ptot; 23 if(!p1[o].r) p1[o].r = ++ptot; 24 if(p1[o].lazy) 25 { 26 p1[p1[o].l].lazy += p1[o].lazy; 27 p1[p1[o].r].lazy += p1[o].lazy; 28 p1[p1[o].l].sum += p1[o].lazy * (mid - l + 1); 29 p1[p1[o].r].sum += p1[o].lazy * (r - mid); 30 p1[o].lazy = 0; 31 } 32 } 33 34 void update1(ll o, ll l, ll r) 35 { 36 ll mid = (l + r) >> 1; 37 if(ql <= l && r <= qr) 38 { 39 p1[o].sum += r - l + 1, p1[o].lazy++; 40 return; 41 } 42 push_down(o, l, r); 43 if(ql <= mid) update1(p1[o].l, l, mid); 44 if(mid < qr) update1(p1[o].r, mid + 1, r); 45 push_up(o, l, r); 46 } 47 48 void update2(ll o, ll l, ll r) 49 { 50 ll mid = (l + r) >> 1; 51 if(!p2[o]) p2[o] = ++ptot; 52 update1(p2[o], 1, n); 53 if(l == r) return; 54 if(val <= mid) update2(o << 1, l, mid); 55 else update2(o << 1 | 1, mid + 1, r); 56 } 57 58 ll query1(ll o, ll l, ll r) 59 { 60 ll mid = (l + r) >> 1, cnt = 0; 61 if(ql <= l && r <= qr) return p1[o].sum; 62 push_down(o, l, r); 63 if(ql <= mid) cnt = query1(p1[o].l, l, mid); 64 if(mid < qr) cnt += query1(p1[o].r, mid + 1, r); 65 return cnt; 66 } 67 68 ll query2(ll o, ll l, ll r, ll rk) 69 { 70 ll mid = (l + r) >> 1; 71 if(l == r) return l; 72 if(!p2[o]) p2[o] = ++ptot; 73 val = query1(p2[o << 1 | 1], 1, n); 74 if(val < rk) return query2(o << 1, l, mid, rk - val); 75 return query2(o << 1 | 1, mid + 1, r, rk); 76 } 77 78 int main() 79 { 80 ll m; 81 scanf("%lld%lld", &n, &m); 82 for(ll i = 1; i <= m; i++) 83 { 84 scanf("%lld%lld%lld%lld", &op[i].op, &op[i].l, &op[i].r, &op[i].val); 85 if(op[i].op == 1) cd[++ctot] = op[i].val; 86 } 87 sort(cd + 1, cd + ctot + 1); 88 for(int i = 1; i <= m; i++) 89 if(op[i].op == 1) 90 op[i].val = lower_bound(cd + 1, cd + ctot + 1, op[i].val) - cd; 91 for(ll i = 1; i <= m; i++) 92 if(op[i].op == 1) 93 { 94 ql = op[i].l, qr = op[i].r, val = op[i].val; 95 update2(1, 1, ctot); 96 } 97 else 98 { 99 ql = op[i].l, qr = op[i].r; 100 printf("%lld\n", cd[query2(1, 1, ctot, op[i].val)]); 101 } 102 return 0; 103 }