基本线段树

线段树可以实现很多的操作。这里就一些最基本的操作进行讨论。

线段树是通过区间合并和lazy标记作为核心来实现log级别的操作的。

区间合并是核心中的核心,你所要支持的操作需要转换成可以进行区间合并的形式。

这里讨论了一些常见操作的维护+区间合并的题目。

数据结构A题

基本上维护和以及平方和,支持赋值、增、乘的操作。

多个操作需要注意优先级:赋值*乘+增。

pushdown的时候,赋值覆盖所有操作,所有子节点的操作恢复初始值。乘会对加标记有影响。

【推断:高优先级对低优先级有影响】

update的时候,再加上这个操作的时候,判断这个操作有没有低优先级的,如果有pushdown再继续。

比如:(a+b)*c,b的标记还没有传下去,加上c的话,会导致变成a*c+b。【因为我们规定了pushdown时计算的优先级】

唯一要注意的是平方和的计算需要列下公式,(a+k)^2,(a*k)^2展开即可知道。

#include<bits/stdc++.h>
	#define FOR(i,l,r) for(int i=l;i<=r;i++)
	#define ll long long
	#define maxn 500050
	#define inf 0x3f3f3f3f3f3f3f3f
	using namespace std;
	
	struct tree2{
		tree2 *lson,*rson;
		ll x,x_2,lazyt=inf,lazyp=0,lazym=1;
	}dizhi[maxn<<2],*root=&dizhi[0];
	
	ll n,m,t=1;
	ll a[maxn];
	ll P=1000000000+7;
	
	void push_up(tree2 *tree,ll l,ll r){
		tree->x=(tree->lson->x+tree->rson->x)%P;
		tree->x_2=(tree->lson->x_2+tree->rson->x_2)%P;
	}
	
	void build(tree2 *tree,ll l,ll r){
		if(l==r){
			tree->x=a[l];
			tree->x_2=(a[l]*a[l])%P;
			return ;
		}
		ll mid=(l+r)>>1;
		tree->lson=&dizhi[t++];
		tree->rson=&dizhi[t++];
		build(tree->lson,l,mid);
		build(tree->rson,mid+1,r);
		push_up(tree,l,r);
	}
	
	void pushdown(tree2 *tree,ll l,ll r){
		//cout<<"*"<<endl;
		if((tree->lazyt==inf&&tree->lazyp==0&&tree->lazym==1)||tree->lson==NULL)return ;
		ll mid=(l+r)>>1;//我们规定优先级=覆盖(A)*B+C->这样会比较方便修改 
		if(tree->lazyt!=inf){
			tree->lson->x=(tree->lazyt*(mid-l+1))%P,tree->rson->x=(tree->lazyt*(r-mid))%P;
			tree->lson->x_2=(mid-l+1)*((tree->lazyt*tree->lazyt)%P)%P;
			tree->rson->x_2=(r-mid)*((tree->lazyt*tree->lazyt)%P)%P;//覆盖 
			tree->lson->lazyt=tree->rson->lazyt=tree->lazyt;
			tree->lson->lazym=tree->rson->lazym=1; 
			tree->lson->lazyp=tree->rson->lazyp=0;
		}//之前子标记说明在之前被修改,一旦遇到现在才传递过来的新覆盖标记,毫无作用了直接清空。 
		tree->lson->x=(tree->lson->x*tree->lazym)%P,tree->rson->x=(tree->rson->x*tree->lazym)%P; 
		tree->lson->x_2=((tree->lson->x_2*tree->lazym)%P*tree->lazym)%P,tree->rson->x_2=((tree->rson->x_2*tree->lazym)%P*tree->lazym)%P;//*
		tree->lson->x_2=(tree->lson->x_2+(ll)2*tree->lazyp*tree->lson->x%P+(mid-l+1)*(tree->lazyp*tree->lazyp%P)%P)%P;
		tree->rson->x_2=(tree->rson->x_2+(ll)2*tree->lazyp*tree->rson->x%P+(r-mid)*(tree->lazyp*tree->lazyp%P)%P)%P;
		//这两行和下面一行实现的是最后的加,(a+k)^2=a^2+2ak+k^2=>a^2和+2k*a和+l*k^2,这里a和是没加之前a和,所以要放前面 
		tree->lson->x=(tree->lson->x+tree->lazyp*(mid-l+1)%P)%P,tree->rson->x=(tree->rson->x+tree->lazyp*(r-mid)%P)%P;
		//下面是:传递标记 
		tree->lson->lazym=(tree->lson->lazym*tree->lazym)%P;
		tree->rson->lazym=(tree->rson->lazym*tree->lazym)%P;
		tree->lson->lazyp=(tree->lson->lazyp*tree->lazym%P+tree->lazyp)%P;
		tree->rson->lazyp=(tree->rson->lazyp*tree->lazym%P+tree->lazyp)%P; 
		tree->lazyt=inf; 
		tree->lazyp=0;
		tree->lazym=1;
	}
	
	void change0(tree2 *tree,ll l,ll r,ll x,ll y,ll d){
		if(x<=l&&y>=r){
			tree->lazyt=d;
			tree->lazym=1;
			tree->lazyp=0;
			tree->x=d*(r-l+1)%P; 
			tree->x_2=(r-l+1)*(d*d%P)%P;
			return ;
		}
		pushdown(tree,l,r);
		ll mid=(l+r)>>1;
		if(x<=mid)change0(tree->lson,l,mid,x,y,d);
		if(y>mid)change0(tree->rson,mid+1,r,x,y,d);
		push_up(tree,l,r); 
	}
	
	void change1(tree2 *tree,ll l,ll r,ll x,ll y,ll d){
		if(x<=l&&y>=r){
			//cout<<tree->x<<endl;
			if(tree->lazyp)pushdown(tree,l,r);//优先级越位,(覆盖*x),如果再*y=>覆盖*(x*y)无影响,如果覆盖*x+k,不传递下去的话,会变成(覆盖*(x*y)+k)错误 
			tree->x=(tree->x*d)%P;
			//cout<<tree->x<<endl;
			tree->x_2=(tree->x_2*((d*d)%P))%P;
			//cout<<tree->x_2<<endl;
			tree->lazym=(tree->lazym*d)%P;
			return ;
		}	
		pushdown(tree,l,r);
		int mid=(l+r)>>1;
		if(x<=mid)change1(tree->lson,l,mid,x,y,d);
		if(y>mid)change1(tree->rson,mid+1,r,x,y,d);
		push_up(tree,l,r); 
	}
	
	void change2(tree2 *tree, ll l,ll r,ll x,ll y,ll d){
		if(x<=l&&y>=r){
			tree->x_2=(tree->x_2+(ll)2*tree->x*d%P+((r-l+1)*(d*d%P))%P)%P;
			//cout<<tree->x_2<<endl;
			tree->x=(tree->x+(ll)d*(r-l+1))%P;
			//cout<<tree->x<<endl;
			tree->lazyp=(tree->lazyp+(ll)d)%P;
			return ; 
		}	
		pushdown(tree,l,r);
		int mid=(l+r)>>1;
		if(x<=mid)change2(tree->lson,l,mid,x,y,d);
		if(y>mid)change2(tree->rson,mid+1,r,x,y,d);
		push_up(tree,l,r);
	}
	
	ll query_x(tree2 *tree,ll l,ll r,ll x,ll y){
		if(x<=l&&y>=r)return tree->x%P;
		pushdown(tree,l,r);
		int mid=(l+r)>>1;
		ll t1=0,t2=0;
		if(x<=mid)t1=query_x(tree->lson,l,mid,x,y);
		if(y>mid)t2=query_x(tree->rson,mid+1,r,x,y);
		return (t1+t2)%P;
	}
	
	ll query_x2(tree2 *tree,ll l,ll r,ll x,ll y){
		if(x<=l&&y>=r)return tree->x_2%P;
		pushdown(tree,l,r);
		int mid=(l+r)>>1;
		ll t1=0,t2=0;
		if(x<=mid)t1=query_x2(tree->lson,l,mid,x,y);
		if(y>mid)t2=query_x2(tree->rson,mid+1,r,x,y);
		return (t1+t2)%P; 
	}
	
	int main(){
		scanf("%lld%lld",&n,&m);
		FOR(i,1,n)scanf("%lld",&a[i]);
		build(root,1,n);
		FOR(i,1,m){
			int mode;
			ll a,b;
			ll c;
			scanf("%d",&mode);
			if(mode==1){
				scanf("%lld%lld%lld",&a,&b,&c);
				change2(root,1,n,a,b,c);
			}
			if(mode==2){
				scanf("%lld%lld%lld",&a,&b,&c);
				change1(root,1,n,a,b,c);
			}
			if(mode==3){
				scanf("%lld%lld%lld",&a,&b,&c);
				change0(root,1,n,a,b,c);
			}
			if(mode==4){
				scanf("%lld%lld",&a,&b);
				ll t1=query_x(root,1,n,a,b);
				ll t2=query_x2(root,1,n,a,b);
				//printf("%lld %lld\n",t1,t2);
				printf("%lld\n",((b-a+1)*t2%P-t1*t1%P+P)%P);
			}
		}	
		return 0;
	}

区间合并好题 HDU1540

对于这个求最长连续1的操作,我们考虑区间合并的时候(pushup),maxlen=a:左孩子左端点开始的最长、b:右孩子右端点开始的最长,c:左孩子右端点开始的+右孩子左端点的最长中最大的。

像这样:1 1 0 1 1 1 0  1 1 1 1          1 1 0 0 1 1 1 0 0 1  1

a=2,b=4+2,c=2

询问操作的时候:需要注意的是,|____x___| |_______|

对于子区间,ll,lr,rl,rr(左孩子左端点开始有多长,左孩子右端点往左连续的有多长)

如果x在lr中了,或者在rl中了,直接返回lr+rl。

否则继续往下,知道最后只有一个点返回这个点的值。【一般到最后的都是0】

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
using namespace std;

#define M 500050

struct tree2{
    tree2 *lson,*rson;
    int fl,fr,ml;
}dizhi[M<<1],*root=&dizhi[0];

int n,m,t=1,a[M];

int Mmax(int a,int b,int c){
    return max(max(a,b),c);
}

void pushup(tree2 *tree,int l,int r){
    int a=tree->lson->fl,b=tree->lson->fr+tree->rson->fl,c=tree->rson->fr;
    tree->ml=Mmax(a,b,c);
    tree->fl=tree->lson->fl;
    tree->fr=tree->rson->fr;
    int mid=(l+r)>>1;
    if(tree->lson->fl==mid-l+1)tree->fl+=tree->rson->fl;
    if(tree->rson->fr==r-mid)tree->fr+=tree->lson->fr;
    //cout<<tree->lson->fl<<" "<<tree->lson->fr<<" "<<tree->rson->fl<<" "<<tree->rson->fr<<endl;
    //cout<<tree->ml<<endl;
}

void build(tree2 *tree,int l,int r){
    if(l==r){
        tree->ml=tree->fl=tree->fr=r-l+1;
        return ;
    }
    int mid=(l+r)>>1;
    tree->lson=&dizhi[t++];
    tree->rson=&dizhi[t++];
    build(tree->lson,l,mid);
    build(tree->rson,mid+1,r);
    pushup(tree,l,r);
}

void update(tree2 *tree,int l,int r,int x,int cnt){
    if(l==r){
        tree->ml=tree->fl=tree->fr=cnt;
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid)update(tree->lson,l,mid,x,cnt);
    else update(tree->rson,mid+1,r,x,cnt);
    pushup(tree,l,r);
}

int query(tree2 *tree,int l,int r,int x){
    if(l==r){
        cout<<tree->ml<<endl;
        return tree->ml;
    }
    int mid=(l+r)>>1;
    if(x<=mid){
        if(x+tree->lson->fr>mid)return tree->lson->fr+tree->rson->fl;
        else return query(tree->lson,l,mid,x);
    }
    else{
        if(x-tree->rson->fl<=mid)return tree->lson->fr+tree->rson->fl;
        else return query(tree->rson,mid+1,r,x);
    }
}

int main(){
    int x;
    char st[10];
    stack<int>s;
    while(cin>>n>>m){
        memset(dizhi,0,sizeof(dizhi));
        build(root,1,n);
        FOR(i,1,m){
            scanf("%s",st);
            switch(st[0]){
                case 'D':
                    scanf("%d",&x);
                    s.push(x);
                    update(root,1,n,x,0);
                    break;
                case 'Q':
                    scanf("%d",&x);
                    printf("%d\n",query(root,1,n,x));
                    break;
                case 'R':
                    if(!s.empty()){
                        x=s.top();s.pop();
                        update(root,1,n,x,1);
                    }
                    break;
            }
        }
        s=stack<int>();
    }
    return 0;
}

数据结构C题

根据上题得到的经验,用LL,LR,RL,RR来维护连通块个数。

cnt=lcnt+rcnt,如果LR、RL都存在,那cnt--;

数据范围较大,需要离散化。离散化的基础上每个点都相隔一个点(因为上述维护操作维护的是连续1的有多少个块,而此题

不把1~3、3~5这种当做一个块,所以隔开避免这种情况)

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
using namespace std;

#define M 500050

int n,m,t=1;
int L[M],R[M];
int A[M<<1],cnt=0;

struct tree2{
    tree2 *lson,*rson;
    int num,fl,fr;
    int lazy=-1;
}dizhi[M<<1],*root=&dizhi[0];

void push_up(tree2 *tree,int l,int r){
    tree->num=tree->lson->num+tree->rson->num;
    if(tree->lson->fr&&tree->rson->fl)tree->num--;
    tree->fl=tree->lson->fl;
    tree->fr=tree->rson->fr;
    int mid=(l+r)>>1;
    if(tree->fl==mid-l+1)tree->fl+=tree->rson->fl;
    if(tree->fr==r-mid)tree->fr+=tree->lson->fr;
}

void push_down(tree2 *tree,int l,int r){
    if(tree->lazy==-1||tree->lson==NULL)return ;
    int mid=(l+r)>>1;
    tree->lson->num=1,tree->lson->fl=tree->lson->fr=mid-l+1;
    tree->rson->num=1,tree->rson->fl=tree->rson->fr=r-mid;
    tree->lson->lazy=tree->rson->lazy=1;
    tree->lazy=-1;
}

void build(tree2 *tree,int l,int r){
    if(l==r){
        tree->num=tree->fl=tree->fr=0;
        return ;
    }
    int mid=(l+r)>>1;
    tree->lson=&dizhi[t++];
    tree->rson=&dizhi[t++];
    build(tree->lson,l,mid);
    build(tree->rson,mid+1,r);
    push_up(tree,l,r);
}

void update(tree2 *tree,int l,int r,int x,int y){
    if(x<=l&&y>=r){
        tree->num=1;
        tree->fl=tree->fr=r-l+1;
        tree->lazy=1;
        return ;
    }
    push_down(tree,l,r);
    //printf("%d%d%d\n",tree->num,tree->lson->num,tree->rson->num);
    //printf("%d %d = %d %d = %d %d\n",tree->fl,tree->fr,tree->lson->fl,tree->lson->fr,tree->rson->fl,tree->rson->fr);
    int mid=(l+r)>>1;
    if(x<=mid)update(tree->lson,l,mid,x,y);
    if(y>mid)update(tree->rson,mid+1,r,x,y);
    push_up(tree,l,r);
}

int main(){
    cin>>n;
    build(root,1,M);
    FOR(i,1,n){
        scanf("%d%d",&L[i],&R[i]);
        A[++cnt]=L[i],A[++cnt]=R[i];
    }
    sort(A+1,A+1+cnt);
    int number=unique(A+1,A+1+cnt)-(A+1);
    FOR(i,1,n){
        L[i]=2*(lower_bound(A+1,A+1+number,L[i])-A)-1;
        R[i]=2*(lower_bound(A+1,A+1+number,R[i])-A)-1;
    }
    FOR(i,1,n){
        update(root,1,M,L[i],R[i]);
        printf("%d\n",root->num);
    }
}

区间覆盖的染色问题类似题目

对于覆盖染色,这也是区间合并的一道好题。

但是对于多个区间询问染色:颜色不能太多,或者是最后询问一次:颜色可以较多。

维护一个关系,每个区间的颜色,如果父区间下有多个颜色,则赋值为-1.

询问的时候遇到-1就往下继续搜,类似于之前对区间和的询问(指定区间继续往下,这样减少了遍历的复杂度)

询问复杂度顶多是颜色*log。这里只给模板啦,很好写的o(* ̄▽ ̄*)ブ要自己写哦

bool vis[35];

struct tree2{
    tree2 *lson,*rson;
    int col,lazy;
}dizhi[M<<1],*root=&dizhi[0];

void push_up(tree2 *tree,int l,int r){
    if(tree->lson->col==tree->rson->col){
        tree->col=tree->lson->col;
    }
    else{
        tree->col=-1;
    }
}

void push_down(tree2 *tree,int l,int r){
    if(!tree->lazy||tree->lson==NULL)return ;
    tree->lson->col=tree->lazy;
    tree->rson->col=tree->lazy;
    tree->lson->lazy=tree->lazy;
    tree->rson->lazy=tree->lazy;
    tree->lazy=0;
}

void build(tree2 *tree,int l,int r){
    if(l==r){
        tree->col=0;
        return ;
    }
    tree->lson=&dizhi[t++];
    tree->rson=&dizhi[t++];
    int mid=(l+r)>>1;
    build(tree->lson,l,mid);
    build(tree->rson,mid+1,r);
    push_up(tree,l,r);
}

void update(tree2 *tree,int l,int r,int x,int y,int d){
    if(x<=l&&r<=y){
        tree->col=d;
        tree->lazy=d;
        return ;
    }
    push_down(tree,l,r);
    int mid=(l+r)>>1;
    if(x<=mid)update(tree->lson,l,mid,x,y,d);
    if(y>mid)update(tree->rson,mid+1,r,x,y,d);
    push_up(tree,l,r);
}

void query(tree2 *tree,int l,int r,int x,int y){
    int mid=(l+r)>>1;
    if(x<=l&&r<=y){
        if(tree->col==-1){
            push_down(tree,l,r);
            query(tree->lson,l,mid,x,y);
            query(tree->rson,mid+1,r,x,y);
        }
        else{
            if(vis[tree->col]==false)
            cnt++,vis[tree->col]=true;
        }
        return ;
    }
    push_down(tree,l,r);
    if(x<=mid)query(tree->lson,l,mid,x,y);
    if(y>mid)query(tree->rson,mid+1,r,x,y);
}

两道题中有一道需要离散化。

对于数据结构C题和这道题的离散化,具体要求是不同。

前者要保证相邻区间隔开,所以直接每个点都插一个点即可。

后者要保证相邻区间不隔开,且每个区间中一定要有中间的长度。|____|__|___|,假设一共覆盖了大区间,左边区间,右边区间。普通离散化会导致区间没有长度,对于这种情况,中间的区间会没有,导致答案错误。

void Lisan(){
    for(int i=1; i<=m; i++) {
        scanf("%d%d",&L[i],&R[i]);
        lsh[2*i-1]=L[i];lsh[2*i]=R[i];
        lsh[i+2*m]=L[i]+1;
    }
    sort(lsh+1 , lsh+3*m+1);
    number = unique(lsh+1 , lsh+3*m+1) - lsh - 1;//unique返回的是:最后的位置往后一位。
    for(int i=1; i<=m; i++)
    {
        L[i] =(lower_bound(lsh+1 , lsh+number+1 , L[i]) - lsh);
        R[i] =(lower_bound(lsh+1,  lsh+number+1 , R[i]) - lsh);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值