BZOJ3065 带插入区间K小值
2016-01-22 10:39
302 查看
因为要求支持插入,所以里层可以套上一个平衡树来维护对应位置的信息。
一般来说平衡树各项操作都是O(logN)的,但是由于外层要维护一个线段树,那么带旋转的平衡树复杂度就难以保证,因为每动一个节点就要在线段树中插入这个节点的子树大小个数的点。(点的深度和子树大小负相关)
带旋转的平衡树最坏情况每次都调整某个点到根的路径,而不带旋转的替罪羊树则是调整整棵子树。
所以带旋转的平衡树中深度越小的点越可能被调整,替罪羊树中深度越小的点越稳定,因此里层平衡树建议选用替罪羊树。同时注意替罪羊树的重构操作时可以用线段树合并,二分答案时可以找到若干棵线段树一起来二分,这样复杂度可以做到O(NLogNLogN)。
值得吐槽的是,没写线段树合并暴力插入节点用了36秒,写线段树合并用了35秒,是我的姿势不对么?
代码看上去有些长?(实际上不长)
代码如下:
一般来说平衡树各项操作都是O(logN)的,但是由于外层要维护一个线段树,那么带旋转的平衡树复杂度就难以保证,因为每动一个节点就要在线段树中插入这个节点的子树大小个数的点。(点的深度和子树大小负相关)
带旋转的平衡树最坏情况每次都调整某个点到根的路径,而不带旋转的替罪羊树则是调整整棵子树。
所以带旋转的平衡树中深度越小的点越可能被调整,替罪羊树中深度越小的点越稳定,因此里层平衡树建议选用替罪羊树。同时注意替罪羊树的重构操作时可以用线段树合并,二分答案时可以找到若干棵线段树一起来二分,这样复杂度可以做到O(NLogNLogN)。
值得吐槽的是,没写线段树合并暴力插入节点用了36秒,写线段树合并用了35秒,是我的姿势不对么?
代码看上去有些长?(实际上不长)
代码如下:
/* * @Author: duyixian * @Date: 2016-01-18 17:29:04 * @Last Modified by: duyixian * @Last Modified time: 2016-01-22 10:08:40 */ #include "cstdio" #include "cstdlib" #include "iostream" #include "algorithm" #include "cstring" #include "queue" #include "vector" #include "cmath" using namespace std; #define MAX_SIZE #define INF 70005 #define Eps #define Mod #define Get(x, a) (x ? x -> a : 0) #define L(i) (i ? Mid + 1 : Left) #define R(i) (i ? Right : Mid) #define Alpha 0.75 #define Traveling(x) for(typeof(x.begin()) it = x.begin(); it != x.end(); ++it) inline int Get_Int() { int Num = 0, Flag = 1; char ch; do { ch = getchar(); if(ch == '-') Flag *= -1; } while(ch < '0' || ch > '9'); do { Num = Num * 10 + ch - '0'; ch = getchar(); } while(ch >= '0' && ch <= '9'); return Num * Flag; } class Segment_Tree_Node { public: Segment_Tree_Node *Child[2]; int Sum; }; class Segment_Tree { public: Segment_Tree_Node *Root; inline Segment_Tree_Node* New() { return (Segment_Tree_Node*)calloc(1, sizeof(Segment_Tree_Node)); } inline void Reclaim(Segment_Tree_Node *&x) { if(x -> Sum) return; free(x); x = NULL; } void Clear(Segment_Tree_Node *x) { if(!x) return; Clear(x -> Child[0]); Clear(x -> Child[1]); free(x); } inline void Clear() { Clear(Root); Root = NULL; } Segment_Tree_Node* Merge(Segment_Tree_Node *x, Segment_Tree_Node *y, int Left, int Right) { if(!y) return x; if(!x) x = New(); if(Left == Right) { x -> Sum += Get(y, Sum); Reclaim(x); return x; } int Mid = Left + Right >> 1; x -> Child[0] = Merge(x -> Child[0], y -> Child[0], L(0), R(0)); x -> Child[1] = Merge(x -> Child[1], y -> Child[1], L(1), R(1)); x -> Sum = Get(x -> Child[0], Sum) + Get(x -> Child[1], Sum); Reclaim(x); return x; } inline void Merge(Segment_Tree x) { Root = Merge(Root, x.Root, 0, INF); } Segment_Tree_Node* Insert(Segment_Tree_Node *x, int Location, int Value, int Left, int Right) { if(!x) x = New(); if(Left == Right) { x -> Sum += Value; return x; } int Mid = Left + Right >> 1, i = Location > Mid; x -> Child[i] = Insert(x -> Child[i], Location, Value, L(i), R(i)); x -> Sum = Get(x -> Child[0], Sum) + Get(x -> Child[1], Sum); Reclaim(x); return x; } inline void Insert(int Location, int Value) { Root = Insert(Root, Location, Value, 0, INF); } }; class Scapegoat_Tree_Node { public: Scapegoat_Tree_Node *Child[2], *Father; int Size, Value; Segment_Tree Sum; inline bool UnBalanced() { return Get(Child[0], Size) > Alpha * Size || Get(Child[1], Size) > Alpha * Size; } inline void Update() { if(Child[0]) Sum.Merge(Child[0] -> Sum); if(Child[1]) Sum.Merge(Child[1] -> Sum); } }; class Scapegoat_Tree { public: Scapegoat_Tree_Node *Root, *Good; vector<Scapegoat_Tree_Node*> temp; vector<Segment_Tree_Node*> Tree; vector<int> Val; inline Scapegoat_Tree_Node* New(int Value) { Scapegoat_Tree_Node *x = (Scapegoat_Tree_Node*)calloc(1, sizeof(Scapegoat_Tree_Node)); x -> Sum.Insert(Value, 1); x -> Value = Value; x -> Size = 1; return x; } Scapegoat_Tree() { Good = (Scapegoat_Tree_Node*)malloc(sizeof(Scapegoat_Tree_Node)); } void Travel(Scapegoat_Tree_Node *x) { if(!x) return; Travel(x -> Child[0]); temp.push_back(x); Travel(x -> Child[1]); } Scapegoat_Tree_Node* Rebuild(int Left, int Right, Scapegoat_Tree_Node *Father) { if(Left > Right) return NULL; int Mid = Left + Right >> 1; Scapegoat_Tree_Node *x = temp[Mid]; x -> Size = Right - Left + 1; x -> Sum.Insert(x -> Value, 1); x -> Child[0] = Rebuild(Left, Mid - 1, x); x -> Child[1] = Rebuild(Mid + 1, Right, x); x -> Update(); x -> Father = Father; return x; } inline void Rebuild(Scapegoat_Tree_Node *x) { if(!x || x == Good) return; Scapegoat_Tree_Node *Father = x -> Father; Travel(x); Traveling(temp) (*it) -> Sum.Clear(); if(Father) Father -> Child[Father -> Child[1] == x] = Rebuild(0, temp.size() - 1, Father); else Root = Rebuild(0, temp.size() - 1, Father); temp.clear(); } inline Scapegoat_Tree_Node* Insert_and_Find_Scapegoat_if_Need(Scapegoat_Tree_Node *x, int Location, int Value, int Depth) { ++x -> Size; x -> Sum.Insert(Value, 1); int i = Location > Get(x -> Child[0], Size) + 1; if(i) Location -= Get(x -> Child[0], Size) + 1; if(x -> Child[i]) { Scapegoat_Tree_Node *Scapegoat = Insert_and_Find_Scapegoat_if_Need(x -> Child[i], Location, Value, Depth + 1); if(Scapegoat) return Scapegoat; else return x -> UnBalanced() ? x : NULL; } else { x -> Child[i] = New(Value); x -> Child[i] -> Father = x; return Depth <= log(Root -> Size) / log(1 / Alpha) ? Good : NULL; } } inline void Insert(int Location, int Value) { if(!Root) Root = New(Value); else Rebuild(Insert_and_Find_Scapegoat_if_Need(Root, Location, Value, 1)); } inline void Change(int Location, int Value) { Scapegoat_Tree_Node *x = Root; while(x) { if(Location <= Get(x -> Child[0], Size)) x = x -> Child[0]; else if(Location == Get(x -> Child[0], Size) + 1) break; else Location -= Get(x -> Child[0], Size) + 1, x = x -> Child[1]; } int K = x -> Value; x -> Value = Value; for(; x; x = x -> Father) x -> Sum.Insert(Value, 1), x -> Sum.Insert(K, -1); } void Find(Scapegoat_Tree_Node *x, int Left, int Right) { if(Left > Right) return; int L = Get(x -> Child[0], Size); if(Left == 1 && Right == x -> Size) { Tree.push_back(x -> Sum.Root); return; } if(Right <= L) Find(x -> Child[0], Left, Right); else if(Left > L + 1) Find(x -> Child[1], Left - L - 1, Right - L - 1); else { Val.push_back(x -> Value); Find(x -> Child[1], 1, Right - L - 1); Find(x -> Child[0], Left, L); } } inline int Ask(int Left, int Right, int K) { Find(Root, Left, Right); int L = 0, R = INF, Sum = 0; while(L < R) { int Sum1 = 0, Sum2 = 0, M = L + R >> 1; Traveling(Tree) Sum1 += Get(Get((*it), Child[0]), Sum); Traveling(Val) if((*it) <= M) ++Sum2; if(Sum + Sum1 + Sum2 < K) { Sum += Sum1; Traveling(Tree) (*it) = Get((*it), Child[1]); L = M + 1; } else { Traveling(Tree) *it = Get((*it), Child[0]); R = M; } } Tree.clear(); Val.clear(); return L; } }Tree; int LastAns, N, Q; int main() { cin >> N; for(int i = 0; i < N; ++i) Tree.temp.push_back(Tree.New(Get_Int())); Traveling(Tree.temp) (*it) -> Sum.Clear(); Tree.Root = Tree.Rebuild(0, Tree.temp.size() - 1, NULL); Tree.temp.clear(); cin >> Q; while(Q--) { char Op[3]; scanf("%s", Op); int a = Get_Int() ^ LastAns, b = Get_Int() ^ LastAns; if(Op[0] == 'Q') printf("%d\n", LastAns = Tree.Ask(a, b, Get_Int() ^ LastAns)); else if(Op[0] == 'M') Tree.Change(a, b); else Tree.Insert(a, b); } return 0; }
相关文章推荐
- 17.php模版模式
- Windows下Mysql 5.7.10不能登陆的问题
- JTS Geometry关系判断和分析
- 蓝桥杯 算法提高 十进制数转八进制数
- Lodarunner11录制脚本时打不开IE
- 设计模式读书笔记-----迭代器模式
- cesar crusius
- GFF3格式文件
- linux下man指令后面的数字的含义
- C++:标准库类型(string、vector、bitset)
- spring mvc @autowired不起作用
- 【干货分享】微信与ERP支付互通 世界会怎样-解决方案推荐
- android文字阴影介绍
- 关于各种项目的SVN 版本控制忽略文件类型搜集
- spring MVC边用边用
- Shell程序设计(三)— shift和select命令的使用
- Ajax跨域请求中的Cookie问题(默认不带cookie等凭证)
- 使用delphi+intraweb进行微信开发3—微信消息处理
- android 自定义滑动按钮
- 最大连接数的配置