恩推倒重写.
线段树就是一棵二叉树,保存区间的左右值,查询时合并结果.这样可以吧线性复杂度转化为$O(log{n})$时间复杂度.
线段树的构造很简单.
struct node{ int left,right,data; struct sign{ int type; } sign; } nodes[200000];
这保存了一个结点.data保存即时询问的东西.sign保存lazy-tags
线段树的初始化即赋初始值.此时无需memset一遍
int build_tree(int index,int left,int right){ nodes[index].left=left; nodes[index].right=right; nodes[index].sign.type=0; //初始值先生 if(left==right){ nodes[index].data=1; hashTreeNode[left]=i; return nodes[index].data; } //如果是点,就没有子结点了.这时初始化 int mid=(left+right)/2,i2=index*2; //如果是线段 return nodes[index].data=build_tree(i2,left,mid)+build_tree(i2+1,mid+1,right); //就递归建树,将初始值相加 }
可以看出这里传递的指针是不会浮动的不会引喻失意[口胡状>引用到非树中结点
int update(int i,int l,int r,int t){ int left,right,mid,tt; left=nodes[i].left; right=nodes[i].right; if(l==left&&r==right){ addTag(i,t);//为i打标记 } mid=left+right; mid=mid/2; if(hasTag(i)){//如果有标记 tt=getTag(i);//返回i标记的值 clearTag(i);//清除i的标记 update( i*2 , left, mid ,tt); update(i*2+1,mid+1,right,tt); //将标记推到子结点,O(1) (刚好完全反应[口胡>恰好是子结点left和right) } if(l<=mid) update( i*2 , l ,min(mid,r),t);//推到子结点进行递推 if(r >mid) update(i*2+1,max(l,mid+1), r ,t);//同理咯 //被完全覆盖的结点总是不会递推到它的子结点的,所以保证了O(log n)因为每层访问两个结点 joinResult(i); //对i进行合并子结点的结果 } //addTag,getTag,clearTag,joinResult均视实际情况自己实现
query的实现
query也是一大问题.一般来说可以通过和update类似的方法来处理.特殊情况可以用奇葩方法处理.
int query(int t,int l,int r,int t){ int left,right,mid,tt=0; left=nodes[i].left; right=nodes[i].right; pushdown(i);//pushdown就是将标记传递给子结点,清空自己的标记并将结果统计在data里 if(l==left&&r==right){ return i.data; } if(l<=mid) tt+=query( i*2 , l ,min(mid,r),t);//统计出tt if(r >mid) tt+=query(i*2+1,max(l,mid+1), r ,t); return tt; }
不保证正确...不过这就是我的总结了.重点在注释部分.
恩特殊情况是----hdu1698
这里由于统计是全局的,实现相当简单.
int query(int t){ if(nodes[t].sign.type || nodes[t].left==nodes[t].right) return nodes[t].data;//再逗逼的OIer也能看得懂 return query(t*2)+query(t*2+1); }
恩比如说我.
恩语文渣渣,常口胡啥的.