题目大意:
写一种数据结构,他可以:
1.查询k在区间内的排名。
2.查询区间内排名为k的值
3.修改某一个值。
4.求k在区间内的前驱。
5.求k在区间内的后继。
思路:本来以为有什么只有神犇才知道的神一般的数据结构来维护它,问了别人之后,发现只是树套树。据说怎么套都行。我见识鄙陋,就只能线段树套Treap了。
这也是第一次写树套树,还1A了,有点开心。
写树套树,一定要确定自己对这两个树及其熟练,加上少量精细的思考,就可以完成树套树。(我只是弱渣,求神犇别D)
具体实现:第一层是线段树,第二层是Treap。由于每个线段树的节点代表实际的一段区间,这样就可以在每个线段树的节点下面接一个Treap,维护这段区间,并可以在O(logn)的时间之内求出这段区间的各种信息。
考虑第一种操作:考虑线段树的工作原理,对于整段区间,就直接在这个区间的Treap上进行操作,然后返回结果。对于不整的区间,递归解决左半部分和右半部分,然后加起来返回。
第二种操作:对于一整段的区间可以直接在Treap上找区间第K大,但是对于不整的区间就无能为力了。所以没办法只能在套一层logn,二分查找这个值。对于二分的mid值,查找这个值在区间内的排名,这显然满足二分性质。要好好讨论一下这个问的边界条件。
第三种操作:递归向下修改,没经过一个线段树的节点,就把这个节点下面接的Treap减去原来位置的那个值,然后加上那个位置变成的值。
最后两种操作:对于整区间,直接返回Treap返回的前驱,后继。不整的区间,递归求出左半部分,右半部分,取最小或最大值,然后返回。
CODE:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 50010 #define INF 1e9 #define LEFT (pos << 1) #define RIGHT (pos << 1|1) #define SIZE(x) ((x == NULL) ? 0:x->size) using namespace std; struct Complex{ int val,cnt,size,random; Complex *son[2]; Complex() { son[0] = son[1] = NULL; cnt = size = 1; random = rand(); } int Compare(int x) { if(x == val) return -1; return x > val; } void Maintain() { size = cnt; if(son[0] != NULL) size += son[0]->size; if(son[1] != NULL) size += son[1]->size; } }; int cnt,asks; int src[MAX]; Complex *tree[MAX << 2]; void Pretreatment(); void BuildTree(int l,int r,int pos); int GetRank(int l,int r,int x,int y,int pos,int k); int GetKth(int x,int y,int pos,int k); void Modify(int l,int r,int aim,int pos,int c); int FindPred(int l,int r,int x,int y,int pos,int k); int FindSucc(int l,int r,int x,int y,int pos,int k); inline void Rotate(Complex *&a,bool dir); void Insert(Complex *&a,int x); void Delete(Complex *&a,int x); int GetRank(Complex *a,int k); int FindPred(Complex *a,int x); int FindSucc(Complex *a,int x); int main() { Pretreatment(); cin >> cnt >> asks; for(int i = 1;i <= cnt; ++i) scanf("%d",&src[i]); BuildTree(1,cnt,1); for(int flag,i = 1;i <= asks; ++i) { scanf("%d",&flag); int x,y,z; if(flag == 1) { scanf("%d%d%d",&x,&y,&z); printf("%d\n",GetRank(1,cnt,x,y,1,z) + 1); } if(flag == 2) { scanf("%d%d%d",&x,&y,&z); printf("%d\n",GetKth(x,y,1,z)); } if(flag == 3) { scanf("%d%d",&x,&y); Modify(1,cnt,x,1,y); src[x] = y; } if(flag == 4) { scanf("%d%d%d",&x,&y,&z); printf("%d\n",FindPred(1,cnt,x,y,1,z)); } if(flag == 5) { scanf("%d%d%d",&x,&y,&z); printf("%d\n",FindSucc(1,cnt,x,y,1,z)); } } return 0; } void Pretreatment() { memset(tree,NULL,sizeof(tree)); } void BuildTree(int l,int r,int pos) { for(int i = l;i <= r; ++i) Insert(tree[pos],src[i]); if(l == r) return ; int mid = (l + r) >> 1; BuildTree(l,mid,LEFT); BuildTree(mid + 1,r,RIGHT); } int GetRank(int l,int r,int x,int y,int pos,int k) { if(l == x && y == r) return GetRank(tree[pos],k); int mid = (l + r) >> 1; if(y <= mid) return GetRank(l,mid,x,y,LEFT,k); if(x > mid) return GetRank(mid + 1,r,x,y,RIGHT,k); int left = GetRank(l,mid,x,mid,LEFT,k); int right = GetRank(mid + 1,r,mid + 1,y,RIGHT,k); return left + right; } int GetKth(int x,int y,int pos,int k) { int L = 0,R = INF,re; while(L <= R) { int mid = (L + R) >> 1; int temp = GetRank(1,cnt,x,y,1,mid); if(temp < k) L = mid + 1; else R = mid - 1; } int temp = GetRank(1,cnt,x,y,1,L); if(temp >= k) L = FindPred(1,cnt,x,y,1,L); return L; } void Modify(int l,int r,int aim,int pos,int c) { Delete(tree[pos],src[aim]); Insert(tree[pos],c); if(l == r) return ; int mid = (l + r) >> 1; if(aim <= mid) Modify(l,mid,aim,LEFT,c); else Modify(mid + 1,r,aim,RIGHT,c); } int FindPred(int l,int r,int x,int y,int pos,int k) { if(l == x && y == r) return FindPred(tree[pos],k); int mid = (l + r) >> 1; if(y <= mid) return FindPred(l,mid,x,y,LEFT,k); if(x > mid) return FindPred(mid + 1,r,x,y,RIGHT,k); int left = FindPred(l,mid,x,mid,LEFT,k); int right = FindPred(mid + 1,r,mid + 1,y,RIGHT,k); return max(left,right); } int FindSucc(int l,int r,int x,int y,int pos,int k) { if(l == x && y == r) return FindSucc(tree[pos],k); int mid = (l + r) >> 1; if(y <= mid) return FindSucc(l,mid,x,y,LEFT,k); if(x > mid) return FindSucc(mid + 1,r,x,y,RIGHT,k); int left = FindSucc(l,mid,x,mid,LEFT,k); int right = FindSucc(mid + 1,r,mid + 1,y,RIGHT,k); return min(left,right); } ////////////////////////////////Treap/////////////////////////// inline void Rotate(Complex *&a,bool dir) { Complex *k = a->son[!dir]; a->son[!dir] = k->son[dir]; k->son[dir] = a; a->Maintain(),k->Maintain(); a = k; } void Insert(Complex *&a,int x) { if(a == NULL) { a = new Complex(); a->val = x; return ; } int dir = a->Compare(x); if(dir == -1) a->cnt++; else { Insert(a->son[dir],x); if(a->son[dir]->random > a->random) Rotate(a,!dir); } a->Maintain(); } void Delete(Complex *&a,int x) { int dir = a->Compare(x); if(dir != -1) Delete(a->son[dir],x); else { if(a->cnt > 1) a->cnt--; else { if(a->son[0] == NULL) a = a->son[1]; else if(a->son[1] == NULL) a = a->son[0]; else { bool _dir = (a->son[0]->random > a->son[1]->random); Rotate(a,_dir); Delete(a->son[_dir],x); } } } if(a != NULL) a->Maintain(); } int GetRank(Complex *a,int k) { if(a == NULL) return 0; if(k <= a->val) return GetRank(a->son[0],k); return SIZE(a->son[0]) + a->cnt + GetRank(a->son[1],k); } int FindPred(Complex *a,int x) { if(a == NULL) return -INF; if(a->val >= x) return FindPred(a->son[0],x); return max(a->val,FindPred(a->son[1],x)); } int FindSucc(Complex *a,int x) { if(a == NULL) return INF; if(a->val <= x) return FindSucc(a->son[1],x); return min(a->val,FindSucc(a->son[0],x)); }