线段树的总结

恩推倒重写.

线段树就是一棵二叉树,保存区间的左右值,查询时合并结果.这样可以吧线性复杂度转化为$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);
}

恩比如说我.


恩语文渣渣,常口胡啥的.

原文地址:https://www.cnblogs.com/tmzbot/p/4047098.html