Segment Tree Beats 区间最值问题

Segment Tree Beats 区间最值问题

线段树一类特殊技巧!

引出:CF671C Ultimate Weirdness of an Array

其实是考试题,改题的时候并不会区间取最值,区间求和,之后秉承着好好学习的态度,学习了Segment tree Beats

套路是维护出区间最小值和次小值,以及区间最小值数量。之后再维护出题目中需要的东西就好了。之后怎么处理呢,如果我们需要维护出区间和x取max,那么,如果x<=minn[rt],那么直接return;如果x<minx[rt](minx[rt]为区间次小值),那么之间将最小值改成x,之后再改需要改的信息就好了...但是,如果x>=minx[rt],那么就递归处理子树信息。

什么?你说它的时间复杂度?每次修改均摊O(nlogn)

什么时候修改是O(n)呢,就是所有点的状态大概接近于1,2,1,2,1,2,1,2,1,2...之后对3取max,这种情况下单次修改是O(n)的,但是修改完这次之后,不论下一次修改是什么,都是O(1)的,并且,求和不会超过O(nlogn)

其实本质上,什么拖累了时间复杂度呢?就是当区间中不同的元素特别多的时候,单次修改的时间复杂度会较为高昂,但是假设,我们修改了A个元素,那么就相当于区间的集合中少了A-1个元素,并且我们知道这次修改最大是Alogn的,那么下一次修改就相当于少了A-1个不同元素,也就是说,假设没有这次修改,下一次修改B个元素的话,那么有了这次修改下一次就相当于修改了B-A+1个元素,那么我们考虑将所有的修改操作的时间复杂度求和,接近于O(nlogn),但是,这并不完全,因为每次修改可能会导致某些区间的元素个数增加。那么我们考虑会增加多少呢?因为所有修改的元素都修改成了一个值,那么就相当于有些区间的元素的个数多了1。那么修改的时间复杂度就是期望上(Qlogn+nlogn),但是其实并不会跑满,而且实际效率很快。

那么线段树的题嘛,考虑加上区间修改等操作的时间复杂度。因为每次区间覆盖的话,非常棒,取消了许多节点的独立性,时间复杂度完全没有问题!但是区间加的话假如一个区间的是1,2,3,4,5,6,1,2,3,4,5,6之后针对前6个区间加6,那么相当于增加了一倍的独立点个数,但是反过来考虑,构成1,2,3,4,5,6,1,2,3,4,5,6需要这种特殊构造,而特殊构造耗费的操作数量是接近n级别的,那么其实反过来考虑一下,时间复杂度没有任何问题,因为,其实我们考虑,只有区间修改的时间复杂度,是logn,那么为什么是O(logn)的原因是因为打了lazy标记,那么其实我们假装这里打了一个lazy标记,那么其实每次修改的节点只有log级别的,那么其实A也只增加了log级别的存在,而每次PushDown增加的也只是logn*Alogn其实也就是Alognlogn的时间复杂度。这非常正确,请不要反驳我。

那么我们考虑完时间复杂度了,是不是该讲题了?

并不是,因为例题没有那么多...

那么我们先举一个例子

给定一个长度为n的数列A,接下来有m次操作:

• 区间[l,r]中的所有数变成min(Ai,x)

• 询问区间[l,r]中所有数的和

• n,m≤50000 分块!(抱歉,我不会!)

• n,m ≤ 500000

裸题,按照上述要求维护一下就可以了,如果想写的话,可以联系我,我这里有我自己出的数据...

给定一个长度为n的数列A,接下来有m次操作:

• 区间[l,r]中的所有数变成min(Ai,x)

• 区间[l,r]中的所有数加上x(x可能是负数)

• 询问区间[l,r]中所有数的和

• n,m≤50000  分块!

• n,m ≤ 500000

同样是裸题,多了一个操作,HDU上有...题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5306

剩下的就是例题时间了!

BZOJ4355: Play with sequence

这个是不是有一点感觉了呢?

分析:其实还是区间操作裸题,我们对于操作1,之间区间覆盖,对于操作2先区间加,再区间取max...其实并不是很难对不对?对于操作3,之间输出1-n的最小值个数(如果最小值是0的话)

附上代码:

#include <queue>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 300505
#define ls rt<<1
#define rs rt<<1|1
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
ll minn[N<<2],minx[N<<2],cn[N<<2],cov[N<<2],add[N<<2];int n,Q;
void PushUp(int rt)
{
	if(minn[ls]<minn[rs])minn[rt]=minn[ls],minx[rt]=min(minn[rs],minx[ls]),cn[rt]=cn[ls];
	else if(minn[ls]==minn[rs])minn[rt]=minn[ls],minx[rt]=min(minx[rs],minx[ls]),cn[rt]=cn[ls]+cn[rs];
	else minn[rt]=minn[rs],minx[rt]=min(minn[ls],minx[rs]),cn[rt]=cn[rs];
}
void PushDown(int m,int rt)
{
	if(cov[rt]!=-1)minn[rs]=minn[ls]=cov[ls]=cov[rs]=cov[rt],cn[ls]=(m-(m>>1)),cn[rs]=(m>>1),minx[ls]=minx[rs]=1ll<<62,add[ls]=add[rs]=0,cov[rt]=-1;
	if(add[rt])minn[ls]+=add[rt],minn[rs]+=add[rt],minx[ls]+=add[rt],minx[rs]+=add[rt],add[ls]+=add[rt],add[rs]+=add[rt],add[rt]=0;
	if(minn[rt]>minn[ls])minn[ls]=minn[rt];if(minn[rt]>minn[rs])minn[rs]=minn[rt];
}
void build(int l,int r,int rt)
{
	cov[rt]=-1,add[rt]=0;
	if(l==r){scanf("%lld",&minn[rt]),minx[rt]=1ll<<62,cn[rt]=1;return;}
	int m=(l+r)>>1;build(lson);build(rson);PushUp(rt);
}
void Update_cov(int L,int R,ll c,int l,int r,int rt)
{
	if(L<=l&&r<=R){minn[rt]=cov[rt]=c,minx[rt]=1ll<<62,cn[rt]=r-l+1,add[rt]=0;return;}PushDown(r-l+1,rt);int m=(l+r)>>1;
	if(L<=m)Update_cov(L,R,c,lson);if(m<R)Update_cov(L,R,c,rson);PushUp(rt);
}
void Update_add(int L,int R,ll c,int l,int r,int rt)
{
	if(L<=l&&r<=R){minn[rt]+=c,add[rt]+=c,minx[rt]+=c;return;}PushDown(r-l+1,rt);int m=(l+r)>>1;
	if(L<=m)Update_add(L,R,c,lson);if(m<R)Update_add(L,R,c,rson);PushUp(rt);
}
void Update_max(int L,int R,ll c,int l,int r,int rt)
{
	if(c<=minn[rt])return ;
	if(L<=l&&r<=R&&minx[rt]>c){minn[rt]=c;return ;}PushDown(r-l+1,rt);int m=(l+r)>>1;
	if(L<=m)Update_max(L,R,c,lson);if(m<R)Update_max(L,R,c,rson);PushUp(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R)return minn[rt]?0:cn[rt];PushDown(r-l+1,rt);int m=(l+r)>>1;ll ret=0;
	if(L<=m)ret+=query(L,R,lson);if(m<R)ret+=query(L,R,rson);return ret;
}
int main()
{
	scanf("%d%d",&n,&Q);
	build(1,n,1);
	while(Q--)
	{
		int x,y,op;ll z;scanf("%d%d%d",&op,&x,&y);
		if(op==1)scanf("%lld",&z),Update_cov(x,y,z,1,n,1);
		else if(op==2)scanf("%lld",&z),Update_add(x,y,z,1,n,1),Update_max(x,y,0ll,1,n,1);
		else printf("%lld\n",query(x,y,1,n,1));
	}
	return 0;
}

  好吧好吧...其实例题目前只有一道...目测还有最假女选手什么的...好麻烦的说...

posted @ 2018-06-14 20:45  Winniechen  阅读(1287)  评论(0编辑  收藏  举报