关于李超线段树和自己的感想

李超线段树用来维护:给你\(n\)条直线,求出当\(x=a\)时的最大值,支持在线插入.

具体实现:

  我们对于线段树上的每一个节点,记录在\(x=mid\)时的最优线段.

  此时我们即将要插入一条线段为\(A=kx+b\),我们计算一下它在中点处的值为\(val\),\(p\)表示我们记录的线段.

  1. \(val>val_p\),我们比较一下斜率.

     如果\(k>k_{p}\),那么右半区间的答案线段\(A\)一定更优,我们需要寻找一个区间使得\(p\)能成为最优线段.那么我们递归左半区间.

     如果\(k\leq k_{p}\),那么左半区间的答案线段\(A\)一定最优,同理我们递归右半区间.

  2. \(val\leq val_p\).

     如果\(k_{p}> k\),那么右半区间的答案线段\(p\)一定更优,我们需要寻找一个区间使得\(A\)能成为最优线段.那么我们递归左半区间.

     如果\(k_{p}\leq k\),那么左半区间的答案线段\(p\)一定最优,同理我们递归右半区间.

询问\(x=a\)

  我们一路递归下来,对于每个区间的最优线段取一个最大值.

合并

  像线段树合并一样,我们将另一棵树的最优线段插入到这棵树内.

  合并时间和空间复杂度应该为\(O(n\log n)\).

  如果只插入那么空间复杂度应该为\(O(n)\),时间复杂度为\(O(n\log n)\).

代码
struct node {
	int x;
	LL b;
}t[M*20];
struct SEG {
	int Ls[M*20],Rs[M*20];
	LL Calc(node a,LL K) {return a.x*K+a.b;}
	void Insert(int &p,int l,int r,node val) {
		if(!p) {
			p=++Tot;
			t[p]=val;
			return;
		}
		if(l==r) {
			if(Calc(val,C[l])>Calc(t[p],C[l]))t[p]=val;
			return;
		}
		int mid=l+r>>1;
		if(Calc(t[p],C[mid])<Calc(val,C[mid])) {
			if(t[p].x<val.x)Insert(Ls[p],l,mid,t[p]);
			else Insert(Rs[p],mid+1,r,t[p]);
			t[p]=val;
		}else {
			if(t[p].x<val.x)Insert(Rs[p],mid+1,r,val);
			else Insert(Ls[p],l,mid,val);
		}
	}
	LL query(int p,int l,int r,int pos) {
		if(!p)return 0LL;
		if(l==r)return Calc(t[p],C[pos]);
		int mid=l+r>>1;
		LL mx=Calc(t[p],C[pos]);
		if(pos<=mid)return max(mx,query(Ls[p],l,mid,pos));
		return max(mx,query(Rs[p],mid+1,r,pos));
	}
	int Merge(int rt,int lt,int l,int r) {
		if(!rt||!lt)return rt|lt;
		Insert(rt,l,r,t[lt]);
		int mid=l+r>>1;
		Ls[rt]=Merge(Ls[rt],Ls[lt],l,mid);
		Rs[rt]=Merge(Rs[rt],Rs[lt],mid+1,r);
		return rt;
	}
}T;
想法:

  考试时想出了一个写法来替代李超线段树,但是常数更大,内存更大,代码更长,还不好合并两棵树.起码我没有想到.

  因为一条线段取到最优值应该是一个连续的区间\([l,r]\).我大胆猜测如果将这个线段与其他线段的最优值做差,那么这应该是一个有极值的函数.

  那么我们可以用三分转二分的方式,求解出一条线段的\([l,r]\),再将这个二分放在线段树上我们就可以在\(n\log n\)的复杂度插入一个线段了.

代码:
void Down(int p) {
	if(tag[p]==-1)return;
	tag[ls]=tag[p];Ls[ls]=Rs[ls]=tag[p];
	tag[rs]=tag[p];Ls[rs]=Rs[rs]=tag[p];
	tag[p]=-1;
}
void Update(int p,int l,int r,int lx,int rx,int num) {
	if(l==lx&&r==rx) {
		tag[p]=num;
		Ls[p]=Rs[p]=num;
		return;
	}
	Down(p);
	int mid=l+r>>1;
	if(rx<=mid)Update(ls,l,mid,lx,rx,num);
	else if(lx>mid)Update(rs,mid+1,r,lx,rx,num);
	else Update(ls,l,mid,lx,mid,num),Update(rs,mid+1,r,mid+1,rx,num);
	Up(p);
}
LL Calc(int num,int now,int k) {
	return suma[num]-1LL*k*sumb[num]-(suma[now]-1LL*k*sumb[now]);
}
int Query(int p,int l,int r,int pos) {
	if(l==r)return tag[p];
	Down(p);
	int mid=l+r>>1;
	if(pos<=mid)return Query(ls,l,mid,pos);
	return Query(rs,mid+1,r,pos);
}
int Query_L(int p,int l,int r,int pos) {
	if(l==r) {
		if(Calc(pos,tag[p],l-Mx)>=0)return -1;
		return l;
	}
	Down(p);
	int mid=l+r>>1;
	LL lx=Calc(pos,Rs[ls],mid-Mx);
	LL rx=Calc(pos,Ls[rs],mid+1-Mx);
	if(lx<=rx||lx<0)return Query_L(ls,l,mid,pos);
	return Query_L(rs,mid+1,r,pos);
}
int Query_R(int p,int l,int r,int pos) {
	if(l==r)return l;
	Down(p);
	int mid=l+r>>1;
	LL lx=Calc(pos,Rs[ls],mid-Mx);
	LL rx=Calc(pos,Ls[rs],mid+1-Mx);
	if(rx<=lx||rx<0)return Query_R(rs,mid+1,r,pos);
	return Query_R(ls,l,mid,pos);
}
void Update_Mi(int now) {
	int resL=Query_L(1,0,Mxx,now);
	if(resL==-1)return;
	int resR=Query_R(1,0,Mxx,now);
	Update(1,0,Mxx,resL,resR,now);
}
上一篇:CentOS7+CDH5.14.0安装CDH错误排查:HBase服务出现 该运行状况测试不良,因为 Service Monitor 未找到活动 Master


下一篇:find和grep的前世和今生