首页 > 代码库 > BZOJ3065 带插入区间K小值

BZOJ3065 带插入区间K小值

3065: 带插入区间K小值

Time Limit: 60 Sec  Memory Limit: 512 MB

Description

从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。
这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。
这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。
这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)
这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。
请你帮一帮伏特吧。
快捷版题意:带插入、修改的区间k小值在线查询。

Input

第一行一个正整数n,表示原来有n只跳蚤排成一行做早操。
第二行有n个用空格隔开的非负整数,从左至右代表每只跳蚤的弹跳力。
第三行一个正整数q,表示下面有多少个操作。
下面一共q行,一共三种操作对原序列的操作:(假设此时一共m只跳蚤)
1. Q x y k: 询问从左至右第x只跳蚤到从左至右第y只跳蚤中,弹跳力第k小的跳蚤的弹跳力是多少。
    (1 <= x <= y <= m, 1 <= k <= y - x + 1)
2. M x val: 将从左至右第x只跳蚤的弹跳力改为val。
    (1 <= x <= m)
3. I x val: 在从左至右第x只跳蚤的前面插入一只弹跳力为val的跳蚤。即插入后从左至右第x只跳蚤是我刚插入的跳蚤。
    (1 <= x <= m + 1)

为了体现在线操作,设lastAns为上一次查询的时候程序输出的结果,如果之前没有查询过,则lastAns = 0。
则输入的时候实际是:
Q _x _y _k ------> 表示 Q _x^lastAns _y^lastAns _k^lastAns
M _x _val  ------> 表示 M _x^lastAns _val^lastAns
I _x _val  ------> 表示 I _x^lastAns _val^lastAns
简单来说就是操作中输入的整数都要异或上一次询问的结果进行解码。

(祝Pascal的同学早日转C++,就不提供pascal版的描述了。)

Output

对于每个询问输出回答,每行一个回答。

 

Sample Input

10
10 5 8 28 0 19 2 31 1 22
30
I 6 9
M 1 11
I 8 17
M 1 31
M 6 26
Q 2 7 6
I 23 30
M 31 7
I 22 27
M 26 18
Q 26 17 31
I 5 2
I 18 13
Q 3 3 3
I 27 19
Q 23 23 30
Q 5 13 5
I 3 0
M 15 27
Q 0 28 13
Q 3 29 11
M 2 8
Q 12 5 7
I 30 19
M 11 19
Q 17 8 29
M 29 4
Q 3 0 12
I 7 18
M 29 27

Sample Output

28
2
31
0
14
15
14
27
15
14

HINT

此题作为一个小小的研究来搞吧~做法有很多~不知道这题究竟有多少种做法。

请自觉O(log^2n),我故意卡块状链表,块链A了的请受我深情一拜……

A掉的同学请在Discuss里面简要说下自己的做法吧~

原序列长度 <= 35000

插入个数 <= 35000,修改个数 <= 70000,查询个数 <= 70000  ,0 <= 每时每刻的权值 <= 70000

由于是OJ上的题,所以数据无梯度。为了防止卡OJ,本题只有4组数据。


这是一道SB数据结构,替罪羊套权值线段树可以解决,膜拜块链与划分树神犇Orz
先说一个卡oj的故事:从前有只菜鸡,他觉得自己的写法十分优秀,但是总会RE,对比了hzwer的写法之后,他很好奇自己的写法为啥开不下结点,于是他在为bzoj贡献了十几发RE之后,发现自己重建替罪羊时忘了回收主席树了,然后,就没有然后了
惨像,已使我目不忍视了。。。
好吧,替罪羊其实很简单,没有任何难度,就是不平衡就重建,如下
inline bool isbad(int rt) {	return sz[rt] * alpha < max(sz[t[rt].l], sz[t[rt].r]);}
alpha是一个简单粗暴的常数,0.6 - 0.7 足矣,当重建复杂度较大时0.7可以再大一点,但是到0.8就已经很不平衡了
替罪羊维护了整个序列,每个结点对应一只跳蚤,这个结点的线段树存它及它的子树的所有跳蚤的权值线段树,如何提取\( [x,y] \)这个区间呢?先考虑如何提取\( [1, x] \)这个区间
void dfs_part(int rt, int pos) {	if (pos == 0) return ;	if (pos == sz[t[rt].l]) {		p[++pcnt] = Pnode(1, root[t[rt].l]);	} else if (pos > sz[t[rt].l]) {		p[++pcnt] = Pnode(1, root[rt]);		p[++pcnt] = Pnode(-1, root[t[rt].r]);		dfs_part(t[rt].r, pos - sz[t[rt].l] - 1);	} else {		dfs_part(t[rt].l, pos);	}}

然后就没有然后了,插入询问都是\( n\log^{2}n \)注意询问时先提取所有区间然后再直接二分


 

技术分享
  1 #include<bits/stdc++.h>  2 using namespace std;  3 template <class _T> inline void read(_T &_x) {  4     int _t; bool flag = false;  5     while ((_t = getchar()) != - && (_t < 0 || _t > 9)) ;  6     if (_t == -) _t = getchar(), flag = true; _x = _t - 0;  7     while ((_t = getchar()) >= 0 && _t <= 9) _x = _x * 10 + _t - 0;  8     if (flag) _x = -_x;  9 } 10 const int maxn = 70010; 11 struct Tnode { 12     int v, l, r; 13 }; 14 namespace T { 15     Tnode t[maxn * 400]; 16     int st[maxn * 400], top, tot; 17     inline int newnode() { 18         if (!top) st[top++] = ++tot; 19         int cur = st[--top]; 20         t[cur].v = t[cur].l = t[cur].r = 0; 21         return cur; 22     } 23     void remove(int rt) { 24         if (!rt) return ; 25         remove(t[rt].l), remove(t[rt].r); 26         st[top++] = rt; 27     } 28     int merge(int a, int b) { 29         if (!a && !b) return 0; 30         int cur = newnode(); 31         t[cur].v = t[a].v + t[b].v; 32         t[cur].l = merge(t[a].l, t[b].l); 33         t[cur].r = merge(t[a].r, t[b].r); 34         return cur; 35     } 36     void insert(int &rt, int l, int r, int pos, int val) { 37         if (!rt) rt = newnode(); t[rt].v += val; 38         if (l == r) return ; 39         int mid = (l + r) >> 1; 40         if (pos <= mid) insert(t[rt].l, l, mid, pos, val); 41         else insert(t[rt].r, mid + 1, r, pos, val); 42         if (!t[rt].v) remove(rt), rt = 0; 43     } 44 } 45 namespace S { 46     const double alpha = 0.7; 47     Tnode t[maxn * 2]; 48     int st[maxn * 2], top, tot; 49     int root[maxn * 2], sz[maxn * 2]; //root for segtree 50     int Root, v[maxn * 2], cnt, badroot; //Root of Scapegoat 51     inline int newnode() { 52         if (!top) st[top++] = ++tot; 53         int cur = st[--top]; 54         t[cur].v = t[cur].l = t[cur].r = 0; 55         return cur; 56     } 57     void remove(int rt) { 58         if (!rt) return ; 59         remove(t[rt].l), remove(t[rt].r); 60         T::remove(root[rt]); 61         st[top++] = rt; 62     } 63     inline bool isbad(int rt) { 64         return sz[rt] * alpha < max(sz[t[rt].l], sz[t[rt].r]); 65     } 66     void print(int rt) { 67         if (!rt) return ; 68         print(t[rt].l); 69         cout << t[rt].v <<   ; 70         print(t[rt].r); 71     } 72     void Print() { 73         cout << "Seq: " ; print(Root); 74         cout << endl; 75     } 76     void insert(int &rt, int pos, int val) { // insert val after pos 77         if (!rt) rt = newnode(), sz[rt] = 0, t[rt].v = val; 78         ++sz[rt]; 79         T::insert(root[rt], 0, maxn, val, 1); 80         if (sz[rt] != 1) { 81             if (pos <= sz[t[rt].l]) insert(t[rt].l, pos, val); 82             else insert(t[rt].r, pos - sz[t[rt].l] - 1, val); 83             if (isbad(rt)) badroot = rt; 84         } 85     } 86     void dfs_node(int rt) { 87         if (!rt) return ; 88         dfs_node(t[rt].l); 89         v[++cnt] = t[rt].v; 90         dfs_node(t[rt].r); 91     } 92     void build(int &rt, int l, int r) { 93         rt = newnode(); 94         int mid = (l + r) >> 1; 95         t[rt].v = v[mid], sz[rt] = r - l + 1; 96         if (l < mid) build(t[rt].l, l, mid - 1); 97         if (mid < r) build(t[rt].r, mid + 1, r); 98         root[rt] = T::merge(root[t[rt].l], root[t[rt].r]); 99         T::insert(root[rt], 0, maxn, t[rt].v, 1);100     }101     void rebuild(int &rt) {102         cnt = 0, dfs_node(rt);103         remove(rt);104         build(rt, 1, cnt);105     }106     void init(int n) {107         for (int i = 1; i <= n; ++i) read(v[i]);108         build(Root, 1, n);109     }110     void Insert(int pos, int val) { // insert before pos & rebuild111         //printf("Insert %d before position %d\n", val, pos);112         badroot = 0;113         insert(Root, pos - 1, val);114         if (badroot) rebuild(badroot);115     }116     int change(int &rt, int pos, int val) {117         int ret = -1;118         if (pos == sz[t[rt].l] + 1) {119             ret = t[rt].v, t[rt].v = val;120             goto Modify_part;121         }122         if (pos <= sz[t[rt].l]) ret = change(t[rt].l, pos, val);123         else ret = change(t[rt].r, pos - sz[t[rt].l] - 1, val);124         Modify_part:T::insert(root[rt], 0, maxn, val, 1),125                     T::insert(root[rt], 0, maxn, ret, -1);126         return ret;127     }128     void Change(int pos, int val) {129         //printf("Change position %d‘s value to %d\n", pos, val);130         change(Root, pos, val);131     }132     struct Pnode {133         int v, rt;134         Pnode() {}135         Pnode(int a, int b):v(a), rt(b) {}136     }p[maxn * 2];137     int pcnt;138     void dfs_part(int rt, int pos) {139         if (pos == 0) return ;140         if (pos == sz[t[rt].l]) {141             p[++pcnt] = Pnode(1, root[t[rt].l]);142         } else if (pos > sz[t[rt].l]) {143             p[++pcnt] = Pnode(1, root[rt]);144             p[++pcnt] = Pnode(-1, root[t[rt].r]);145             dfs_part(t[rt].r, pos - sz[t[rt].l] - 1);146         } else {147             dfs_part(t[rt].l, pos);148         }149     }150     int Query(int l, int r, int k) {151         //printf("Query %d%s smallest in range [%d, %d]\n", k, k%10 == 1 ? "st" : (k%10 == 2 ? "nd" : (k%10 == 3 ? "rd" : "th")), l, r);152         pcnt = 0;153         dfs_part(Root, l - 1);154         for (int i = 1; i <= pcnt; ++i) p[i].v = -p[i].v;155         dfs_part(Root, r);156         int L = 0, R = maxn, mid, totv;157         while (L < R) {158             mid = (L + R) >> 1, totv = 0;159             for (int i = 1; i <= pcnt; ++i) totv += p[i].v * T::t[T::t[p[i].rt].l].v;160             //cout << mid << ‘ ‘ << tot << endl;161             if (totv >= k) {162                 for (int i = 1; i <= pcnt; ++i) p[i].rt = T::t[p[i].rt].l;163                 R = mid;164             } else {165                 for (int i = 1; i <= pcnt; ++i) p[i].rt = T::t[p[i].rt].r;166                 k -= totv, L = mid + 1;167             }168         }169         return L;170     }171 }172 using S::Print;173 int n, q;174 int main() {175     //freopen("3065.in", "r", stdin);176     //freopen("3065.out", "w", stdout);177     read(n);178     S::init(n);179     //Print();180     read(q);181     char op[20];182     int lastans = 0;183     for (int i = 1, a, b, c; i <= q; ++i) {184         scanf("%s", op);185         switch (op[0]) {186             case Q:187                 read(a), read(b), read(c);188                 a ^= lastans, b ^= lastans, c ^= lastans;189                 printf("%d\n", lastans = S::Query(a, b, c));190                 break;191             case M:192                 read(a), read(b);193                 a ^= lastans, b ^= lastans;194                 S::Change(a, b);195                 break;196             case I:197                 read(a), read(b);198                 a ^= lastans, b ^= lastans;199                 S::Insert(a, b);200                 break;201         }202         //Print();203     }204     return 0;205 }
View Code

 

 

BZOJ3065 带插入区间K小值