线段树基础

终于完成了线段树的一些基础部分
总结一下吧

线段树基础

基本原理

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。(来自百度百科)

建树

简单来说就是将一段区间不断二分至单个元素赋值,再回溯,回溯时求出每个合并后区间和

void build(int p,int l,int r){
    if(l==r){tr[p]==a[l];return;}
    build(ls,l,mid);build(rs,mid+1,r);
    tr[p]=tr[ls]+tr[rs];
}

单点修改

二分查找需要修改的元素,回溯过程中求和

void modify(int p,int l,int r,int pos,int x){
    if(l==r){tr[p]+=x;return;}
    if(pos<=mid)modify(ls,l,mid,pos,x);
    else modify(rs,mid+1,r,pos,x);
    tr[p]=tr[ls]+tr[rs];
}

区间查询

首先这里为什么不讲单点查询呢
因为没必要
有这功夫你为什么不直接访问数组下标???
那么区间查询怎么做呢
对于某一个区间,显然的,这个区间在线段树某个节点上有几种情况:
1.全在左儿子
2.全在右儿子
3.正好是这个区间或大于这个区间
4.横跨两个儿子
5.完全和区间不重合
但是显然
没那么复杂
上面的情况可以直接分为三种
1.需要继续算的
2.直接返回结果的
3.返回零的
于是这就简单了:若是查找到2,就直接返回,若是1就分左右儿子继续查找,若是3就如字面所示

ll query(int p,int l,int r,int ql,int qr){
    if(l>qr||r<ql)return 0;
    if(l>=ql&&r<=qr)return tr[p];
    return query(ls,l,mid,ql,qr)+query(rs,mid+1,r,ql,qr);
}

区间修改

暴力!直接按区间单点修改!
真的是这样么
TLE
显然如果按上面那么做的话复杂度最终会是O(nlogn),那还不如不优化了
所以这里引入一个有趣的标记:懒标记
首先呢,我们先讲个故事吧

从前有一个父亲和两个儿子,在一个新年夜,父亲望着手中即将交给儿子们的压岁钱,陷入了沉思:我是否要将压岁钱给我的儿子们呢,如果这笔钱留在我手里的话,我岂不是就能拥有更大的财富?于是父亲将他的儿子们召集到他身边,说:“我亲爱的儿子们,我要留下这些钱,你们应该没有意见吧”儿子们异口同声的回答:“没问题,但是如果同学问起的话我们该怎么办呢”父亲回答道:“到时我再给你们”

这真是一个悲伤的故事那么听完这个故事,你就会懒标记了!!!
没错,这就是懒标记,对于每次更新,我们不将值全部下放,而是存到父亲的懒标记中,当需要时,如更新或查询时,我们将懒标记下放即可

void push_down(int p,int l,int r){
    tr[ls]+=lz[p]*(mid-l+1);tr[rs]+=lz[p]*(r-mid);
    lz[ls]+=lz[p];lz[rs]+=lz[p];
    lz[p]=0;//一定记得要清零,多少OIer血的教训
}
void modify_area(int p,int l,int r,int ml,int mr,int x){
    if(l>=ml&&r<=qr){tr[p]+=x*(r-l+1);lz[p]+=x;return;}
    push_down(p,l,r);
    modify_area(ls,l,mid,ml,mr,x);modify_area(rs,mid+1,r,ml,mr,x);
    tr[p]=tr[ls]+tr[rs];
}
ll query(int p,int l,int r,int ql,int qr){
    if(l>qr||r<ql)return 0;
    if(l>=ql&&r<=qr)return tr[p];
    push_down(p,l,r);
    return query(ls,l,mid,ql,qr)+query(rs,mid+1,r,ql,qr);
}

全部模板(附带快读)

#include<bits/stdc++.h>
#define ll long long
#define ls (p<<1)
#define rs (p<<1|1)
#define mid(l+r<<1)
#define N 1000005
using namespace std;
inline ll read(){
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
ll tr[4*N],a[N],lz[4*N];
void push_down(int p,int l,int r){
    tr[ls]+=lz[p]*(mid-l+1);tr[rs]+=lz[p]*(r-mid);
    lz[ls]+=lz[p];lz[rs]+=lz[p];
    lz[p]=0;//一定记得要清零,多少OIer血的教训
}
void build(int p,int l,int r){
    if(l==r){tr[p]==a[l];return;}
    build(ls,l,mid);build(rs,mid+1,r);
    tr[p]=tr[ls]+tr[rs];
}
void modify(int p,int l,int r,int pos,int x){
    if(l==r){tr[p]+=x;return;}
    if(pos<=mid)modify(ls,l,mid,pos,x);
    else modify(rs,mid+1,r,pos,x);
    tr[p]=tr[ls]+tr[rs];
}
void modify_area(int p,int l,int r,int ml,int mr,int x){
    if(l>=ml&&r<=qr){tr[p]+=x*(r-l+1);lz[p]+=x;return;}
    push_down(p,l,r);
    modify_area(ls,l,mid,ml,mr,x);modify_area(rs,mid+1,r,ml,mr,x);
    tr[p]=tr[ls]+tr[rs];
}
ll query(int p,int l,int r,int ql,int qr){
    if(l>qr||r<ql)return 0;
    if(l>=ql&&r<=qr)return tr[p];
    push_down(p,l,r);
    return query(ls,l,mid,ql,qr)+query(rs,mid+1,r,ql,qr);
}
int main(){
    ……
    return 0;
}

小结

那么到这里为止,线段树基本原理部分就结束了,我们将会进入基础练习阶段,乘法什么的请各位大佬自行研究(滑稽)

基础练习

A.P1438 无聊的数列

思路

即以首项为k公差为d的等差数列修改序列,考虑差分,可得出被修改差分序列中每项加d,首项加k,末项后一项减长度乘k乘d,求序列每一项,考虑差分序列上进行前缀和,优化代码时间复杂度考虑线段树,时间复杂度(mlogn)

代码
#include<bits/stdc++.h>
using namespace std;
long long n,m,b[100010];
long long tr[400010],a[100010],lz[400010]={0},lz1[400010];
void build(long long p,long long l,long long r){
	lz1[p]=1;
	if(l==r){
		tr[p]=b[l];
		return;
	}
	long long mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	tr[p]=(tr[p*2]+tr[p*2+1]);
	return;
}
void pushdown(long long p,long long l,long long r){
	long long mid=(l+r)/2;
	tr[p*2]=(tr[p*2]*lz1[p]+lz[p]*(mid-l+1));
	tr[p*2+1]=(tr[p*2+1]*lz1[p]+lz[p]*(r-mid));
	lz[p*2]=(lz[p*2]*lz1[p]+lz[p]);
	lz[p*2+1]=(lz[p*2+1]*lz1[p]+lz[p]);
	lz1[p*2]=(lz1[p*2]*lz1[p]);
	lz1[p*2+1]=(lz1[p*2+1]*lz1[p]);
	lz[p]=0;
	lz1[p]=1;
	return;
}
void modify_area(long long p,long long l,long long r,long long ml,long long mr,long long x){
	if(mr<l||r<ml) return;
	if(l>=ml&&r<=mr){
		tr[p]=(tr[p]+x*(r-l+1));
		lz[p]=(lz[p]+x);
		return;	
	}
	long long mid=(l+r)/2;
	pushdown(p,l,r);
	modify_area(p*2,l,mid,ml,mr,x);
	modify_area(p*2+1,mid+1,r,ml,mr,x);
	tr[p]=(tr[p*2]+tr[p*2+1]);
	return;
}
void modify_area_multiplication(long long p,long long l,long long r,long long ml,long long mr,long long x){
	if(mr<l||r<ml) return;
	if(ml<=l&&r<=mr){
		tr[p]=(tr[p]*x);
		lz[p]=(lz[p]*x);
		lz1[p]=(lz1[p]*x);
		return;	
	}
	long long mid=(l+r)/2;
	pushdown(p,l,r);
	modify_area_multiplication(p*2,l,mid,ml,mr,x);
	modify_area_multiplication(p*2+1,mid+1,r,ml,mr,x);
	tr[p]=(tr[p*2]+tr[p*2+1]);
	return;
}
long long query(long long p,long long l,long long r,long long ql,long long qr){
	if(qr<l||r<ql) return 0;
	if(l>=ql&&r<=qr){
		return tr[p];	
	}
	long long mid=(l+r)/2;
	pushdown(p,l,r);
	return (query(p*2,l,mid,ql,qr)+query(p*2+1,mid+1,r,ql,qr));
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		b[i]=a[i]-a[i-1];
	}
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int type,l,r,k,d,p;
		scanf("%d",&type);
		if(type==1){
			scanf("%d%d%d%d",&l,&r,&k,&d);
			modify_area(1,1,n,l,l,k);
			modify_area(1,1,n,r+1,r+1,-(k+(r-l)*d));
			modify_area(1,1,n,l+1,r,d);
		}
		else if(type==2){
			scanf("%d",&p);
			printf("%lld\n",query(1,1,n,1,p));	
		}
		/*for(int i=1;i<=4*n;i++)
			printf("%d ",tr[i]);
		printf("\n");*/
	}
	return 0;
}

B.P4513 小白逛公园

思路

对于查询,考虑最大子段和,对于每段需要查询的序列,最大价值分三种情况:1.全在左2.全在右3.横跨,显然对于每种情况解决方法不同,1、2:直接返回左序列或右序列的价值,3:返回左区间的右加右区间的左,则需要维护每一段的总和、左、右、最大值,需要修改价值,考虑线段树,时间复杂度(mlogn)

代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[500100];
struct park{
    int sum,l,r,maxn;
}tr[2000100];
inline int read(){
	int x=0,f=1;char c;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
park merge(park x,park y){
    return (park){x.sum+y.sum,max(x.l,x.sum+y.l),max(y.r,y.sum+x.r),max(max(x.maxn,y.maxn),x.r+y.l)};
}
void build(int p,int l,int r){
	if(l==r){
		tr[p].sum=tr[p].l=tr[p].r=tr[p].maxn=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	tr[p]=merge(tr[p*2],tr[p*2+1]);
}
void modify(int p,int l,int r,int pos,int x){
	if(l==r){
		tr[p].sum=tr[p].l=tr[p].r=tr[p].maxn=x;
		return;
	}
	int mid=(l+r)/2;
	if(pos<=mid) modify(p*2,l,mid,pos,x);
	else modify(p*2+1,mid+1,r,pos,x);
	tr[p]=merge(tr[p*2],tr[p*2+1]);
}
park query(int p,int l,int r,int ql,int qr){
	if(l>=ql&&r<=qr){
		return tr[p];
	}
	int mid=(l+r)/2;
	if(qr<=mid) return query(p*2,l,mid,ql,qr);
	else if(ql>=mid+1) return query(p*2+1,mid+1,r,ql,qr);
	return merge(query(p*2,l,mid,ql,qr),query(p*2+1,mid+1,r,ql,qr));	
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int u=read(),ql,qr,pos,x;
		if(u==1){
			ql=read();qr=read();
			if(ql>qr) swap(ql,qr);
			printf("%d\n",query(1,1,n,ql,qr).maxn);
		}
		else if(u==2){
			pos=read();x=read();
			modify(1,1,n,pos,x);
		}
	}
	return 0;
}

C.P1471 方差&P5412区间方差

思路

对于方差公式,我们将其展开
在这里插入图片描述
所以当我们维护区间加时
在这里插入图片描述
对于上述公式及推论,维护区间平方和及区间和即可

代码
#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
using namespace std;
int n,m;
double a[N],tr[N*4],ptr[N*4],lz[N*4];
inline int read(){
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
void build(int p,int l,int r){
    if(l==r){tr[p]=a[l];ptr[p]=a[l]*a[l];return;}
    build(ls,l,mid);build(rs,mid+1,r);
    tr[p]=tr[ls]+tr[rs];
    ptr[p]=ptr[ls]+ptr[rs];
}
void push_down(int p,int l,int r){
    ptr[ls]=ptr[ls]+2*lz[p]*tr[ls]+(mid-l+1)*lz[p]*lz[p];ptr[rs]=ptr[rs]+2*lz[p]*tr[rs]+(r-mid)*lz[p]*lz[p];
    tr[ls]+=lz[p]*(mid-l+1);tr[rs]+=lz[p]*(r-mid);
    lz[ls]+=lz[p];lz[rs]+=lz[p];lz[p]=0;
}
void modify_area(int p,int l,int r,int ml,int mr,double x){
    if(mr<l||r<ml)return;
    if(l>=ml&&r<=mr){ptr[p]=ptr[p]+2*x*tr[p]+x*x*(r-l+1);
    tr[p]+=x*(r-l+1);lz[p]+=x;return;}
    push_down(p,l,r);
    modify_area(ls,l,mid,ml,mr,x);modify_area(rs,mid+1,r,ml,mr,x);
    ptr[p]=ptr[ls]+ptr[rs];
    tr[p]=tr[ls]+tr[rs];    
}
double query1(int p,int l,int r,int ql,int qr){
    if(l>qr||r<ql)return 0;
    if(l>=ql&&r<=qr){return tr[p];}
    push_down(p,l,r);
    return query1(ls,l,mid,ql,qr)+query1(rs,mid+1,r,ql,qr);
}
double query2(int p,int l,int r,int ql,int qr){
    if(l>qr||r<ql)return 0;
    if(l>=ql&&r<=qr){return ptr[p];}
    push_down(p,l,r);
    return query2(ls,l,mid,ql,qr)+query2(rs,mid+1,r,ql,qr);
}
double average(int p,int l,int r,int ql,int qr){return query1(p,l,r,ql,qr)/(qr-ql+1);}
double variance(int p,int l,int r,int ql,int qr){return query2(p,l,r,ql,qr)/(qr-ql+1)-(query1(1,1,n,ql,qr)/(qr-ql+1))*(query1(1,1,n,ql,qr)/(qr-ql+1));}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++)scanf("%lf",&a[i]);
    build(1,1,n);
    for(int t=1;t<=m;t++){
        int type=read(),x=read(),y=read();
        double k;
        if(type==1){scanf("%lf",&k);modify_area(1,1,n,x,y,k);}
        if(type==2){printf("%.4lf\n",average(1,1,n,x,y));}
        if(type==3){printf("%.4lf\n",variance(1,1,n,x,y));}
    }
    return 0;
}

D.P6327 区间加区间sin和

思路

前置知识:两角和sin值,直接维护sin、cos,懒标记维护角的大小
但是有一点需要非常注意,更新sin和cos需要用到之前的sin和cos,所以要提前记录之前的sin值cos值

代码
#include<bits/stdc++.h>
using namespace std;
long long n,m;
double str[800010],ctr[800010],a[200010],lz[800010],c[800010];
void build(long long p,long long l,long long r){
	if(l==r){str[p]=sin(a[l]);ctr[p]=cos(a[l]);return;}
	long long mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	str[p]=(str[p*2]+str[p*2+1]);
	ctr[p]=(ctr[p*2]+ctr[p*2+1]);
}
void pushdown(long long p,long long l,long long r){
	double record=str[p*2],record1=str[p*2+1];
	str[p*2]=str[p*2]*cos(lz[p])+sin(lz[p])*ctr[p*2];
	str[p*2+1]=str[p*2+1]*cos(lz[p])+sin(lz[p])*ctr[p*2+1];
	ctr[p*2]=ctr[p*2]*cos(lz[p])-record*sin(lz[p]);
	ctr[p*2+1]=ctr[p*2+1]*cos(lz[p])-record1*sin(lz[p]);
	lz[p*2]+=lz[p];
	lz[p*2+1]+=lz[p];
	lz[p]=0;
	return;
}
void modify_area(long long p,long long l,long long r,long long ml,long long mr,double x){
	if(mr<l||r<ml) return;
	if(l>=ml&&r<=mr){
		double record=str[p];
		str[p]=str[p]*cos(x)+sin(x)*ctr[p];
		ctr[p]=ctr[p]*cos(x)-sin(x)*record;
		lz[p]+=x;
		return;	
	}
	long long mid=(l+r)/2;
	pushdown(p,l,r);
	modify_area(p*2,l,mid,ml,mr,x);
	modify_area(p*2+1,mid+1,r,ml,mr,x);
	str[p]=(str[p*2]+str[p*2+1]);
	ctr[p]=(ctr[p*2]+ctr[p*2+1]);
	return;
}
double query(long long p,long long l,long long r,long long ql,long long qr){
	if(qr<l||r<ql) return 0;
	if(l>=ql&&r<=qr){return str[p];}
	long long mid=(l+r)/2;
	pushdown(p,l,r);
	return (query(p*2,l,mid,ql,qr)+query(p*2+1,mid+1,r,ql,qr));
}
int main(){
	scanf("%lld",&n);
	for(long long i=1;i<=n;i++)scanf("%lf",&a[i]);
	build(1,1,n);
	scanf("%lld",&m);
	for(long long i=1,u,ml,mr,ql,qr;i<=m;i++){
		scanf("%lld",&u);
		double x;
		if(u==1){
			scanf("%lld%lld%lf",&ml,&mr,&x);
			modify_area(1,1,n,ml,mr,x);
		}
		else if(u==2){
			scanf("%lld%lld",&ql,&qr);
			printf("%.1lf\n",query(1,1,n,ql,qr));
		}
	}
	return 0;	
}

E.CF438D The Child and Sequence

思路
代码
#include<bits/stdc++.h>
using namespace std;
const long long P=20200118;
long long n,m;
long long tr[400010],a[100010],lz[400010],maxtr[400010];
void build(long long p,long long l,long long r){
	if(l==r){
		tr[p]=a[l];
        maxtr[p]=a[l];
		return;
	}
	long long mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	tr[p]=(tr[p*2]+tr[p*2+1]);
    maxtr[p]=max(maxtr[p*2],maxtr[p*2+1]);
}
void modify(long long p,long long l,long long r,long long pos,long long x){
	if(l==r){
		tr[p]=x;
        maxtr[p]=x;
		return;
	}
	long long mid=(l+r)/2;
	if(pos<=mid) modify(p*2,l,mid,pos,x);
	else modify(p*2+1,mid+1,r,pos,x);
	tr[p]=(tr[p*2]+tr[p*2+1]);
    maxtr[p]=max(maxtr[p*2],maxtr[p*2+1]);
}
void modify_area(long long p,long long l,long long r,long long ml,long long mr,long long x){
    if(maxtr[p]<x)return;
	if(l==r){
		tr[p]%=x;
        maxtr[p]%=x;
		return;
	}
	long long mid=(l+r)/2;
	if(mr<=mid) modify_area(p*2,l,mid,ml,mr,x);
	else if(ml>=mid+1) modify_area(p*2+1,mid+1,r,ml,mr,x);
	else{
		modify_area(p*2,l,mid,ml,mid,x);
		modify_area(p*2+1,mid+1,r,mid+1,mr,x);
	}
	tr[p]=tr[p*2]+tr[p*2+1];
    maxtr[p]=max(maxtr[p*2],maxtr[p*2+1]);
}
long long query(long long p,long long l,long long r,long long ql,long long qr){
	if(l>=ql&&r<=qr) return tr[p];
	//printf("%lld %lld %lld %lld\n",l,r,ql,qr);
	long long mid=(l+r)/2;
	if(qr<=mid) return query(p*2,l,mid,ql,qr);
	else if(ql>=mid+1) return query(p*2+1,mid+1,r,ql,qr);
	else return query(p*2,l,mid,ql,qr)+query(p*2+1,mid+1,r,ql,qr);
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(long long i=1;i<=n;i++) scanf("%lld",&a[i]);
	build(1,1,n);
	for(long long i=1,u,ml,mr,x,ql,qr;i<=m;i++){
		scanf("%lld",&u);
		if(u==1){
            scanf("%lld%lld",&ql,&qr);
			printf("%lld\n",query(1,1,n,ql,qr));
		}
        else if(u==2){
            scanf("%lld%lld%lld",&ml,&mr,&x);
			modify_area(1,1,n,ml,mr,x);
        }
		else if(u==3){
			scanf("%lld%lld",&ml,&x);
            modify(1,1,n,ml,x);
		}
	}
	return 0;	
}

F.P4145 上帝造题的七分钟2

思路
代码
#include<bits/stdc++.h>
#define ll long long
#define N 100005
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
using namespace std;
inline ll read(){
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
struct tree{ll sum,maxn;}tr[4*N];
ll a[N];
tree merge(tree x,tree y){return (tree){x.sum+y.sum,max(x.maxn,y.maxn)};}
void build(int p,int l,int r){
	if(l==r){tr[p].sum=tr[p].maxn=a[l];return;}
	build(ls,l,mid);build(rs,mid+1,r);
	tr[p]=merge(tr[ls],tr[rs]);
}
void modify_area(int p,int l,int r,int ml,int mr){
	if(l>mr||r<ml||tr[p].maxn==1)return;
	if(l==r){tr[p].sum=tr[p].maxn=sqrt(tr[p].sum);return;}
	modify_area(ls,l,mid,ml,mr);
	modify_area(rs,mid+1,r,ml,mr);
	tr[p]=merge(tr[ls],tr[rs]);
}
tree query(int p,int l,int r,int ql,int qr){
	if(l>qr||r<ql)return (tree){0,0};
	if(l>=ql&&r<=qr)return tr[p];
	return merge(query(ls,l,mid,ql,qr),query(rs,mid+1,r,ql,qr));
}
int main(){
	ll n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	ll m=read();
	for(int i=1;i<=m;i++){
		ll k=read(),l=read(),r=read();
		if(l>r)swap(l,r);
		if(!k)modify_area(1,1,n,l,r);
		else printf("%lld\n",query(1,1,n,l,r).sum);
	}
	return 0;
}

进阶练习

A.P4269 Snow Boots G

思路
代码
#include<bits/stdc++.h>
#define N 100005
#define ll long long
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
using namespace std;
inline ll read(){
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
struct tree{ll sum,l,r,maxn;}tr[4*N];
struct boots{ll s,d,id;}b[4*N];
ll f[N],res[N];
bool cmp(boots x,boots y){if(x.s!=y.s)return x.s<y.s;else return x.d<y.d;}
tree merge(tree x,tree y){
	return (tree){x.sum+y.sum,
	x.maxn==x.sum?x.l+y.l:x.l,
	y.maxn==y.sum?x.r+y.r:y.r,
	max(x.maxn,max(y.maxn,x.r+y.l))};
}
void build(int p,int l,int r){
	if(l==r){tr[p].sum=tr[p].l=tr[p].r=tr[p].maxn=1;return;}
    build(ls,l,mid);build(rs,mid+1,r);
    tr[p]=merge(tr[ls],tr[rs]);
}
void modify(int p,int l,int r,int pos,int x){
	if(l==r){tr[p].l=tr[p].r=tr[p].maxn=x;return;}
	if(pos<=mid)modify(ls,l,mid,pos,x);
	else modify(rs,mid+1,r,pos,x);
	tr[p]=merge(tr[ls],tr[rs]);
}
int main(){
	ll n=read(),m=read();
	for(int i=1;i<=n;i++){b[i].s=read();b[i].d=0;b[i].id=i;}
	for(int i=n+1;i<=n+m;i++){
        ll x=read(),y=read();
        b[i].s=x,b[i].d=y,b[i].id=i;
    }
	build(1,1,n);
	sort(b+1,b+m+n+1,cmp);
	for(int i=1;i<=m+n;i++){
        if(!b[i].d) modify(1,1,n,b[i].id,0);
		else{
			ll cnt=b[i].id-n;
			res[cnt]=tr[1].maxn<b[i].d;
		}
	}
	for(int i=1;i<=m;i++)printf("%lld\n",res[i]);
	return 0;
}

B.P2894 Hotel G

思路
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=50000+10;
int n,m;

struct node{
	int sum,lmax,rmax,lazy,len;
	#define mid (l+r>>1)
	#define lson (rt<<1)
	#define rson (rt<<1|1)
	#define sum(rt) tree[rt].sum
	#define lmax(rt) tree[rt].lmax
	#define rmax(rt) tree[rt].rmax
	#define lazy(rt) tree[rt].lazy
	#define len(rt) tree[rt].len
}tree[maxn<<2];

void pushup(int rt){
	lmax(rt)=(lmax(lson)==len(lson))?len(lson)+lmax(rson):lmax(lson);
	rmax(rt)=(rmax(rson)==len(rson))?len(rson)+rmax(lson):rmax(rson);
	sum(rt)=max(rmax(lson)+lmax(rson),max(sum(lson),sum(rson)));
}

void pushdown(int rt){
	if(!lazy(rt)) return ;
	lazy(lson)=lazy(rson)=lazy(rt);
	sum(lson)=lmax(lson)=rmax(lson)=(lazy(rt)==1)?len(lson):0;
	sum(rson)=lmax(rson)=rmax(rson)=(lazy(rt)==1)?len(rson):0;
	lazy(rt)=0;
}

void build(int l,int r,int rt){
	sum(rt)=lmax(rt)=rmax(rt)=len(rt)=r-l+1;
	if(l == r) return ;
	build(l,mid,lson);
	build(mid+1,r,rson);
}
void update(int L,int R,int tag,int l,int r,int rt){
	if(L <= l && r <= R){
		sum(rt)=lmax(rt)=rmax(rt)=(tag==1)?len(rt):0;
		lazy(rt)=tag;
		return ;
	}
	pushdown(rt);
	if(L <= mid) update(L,R,tag,l,mid,lson);
	if(R > mid) update(L,R,tag,mid+1,r,rson);
	pushup(rt);
}
int query(int len,int l,int r,int rt){
	if(l == r) return l;
	pushdown(rt);
	if(sum(lson)>=len) return query(len,l,mid,lson);
	if(rmax(lson)+lmax(rson)>=len) return mid-rmax(lson)+1;
	return query(len,mid+1,r,rson);
}
int main()
{
	scanf("%d%d",&n,&m);
	build(1,n,1);
	int opt,l,x;
	while(m--){
		scanf("%d",&opt);
		if(opt==1){
			scanf("%d",&x);
			if(sum(1)>=x){
				l=query(x,1,n,1);
				printf("%d\n",l);
				update(l,l+x-1,2,1,n,1);
			}
			else printf("0\n");
		}
		else {
			scanf("%d%d",&l,&x);
			update(l,l+x-1,1,1,n,1);
		}
	}
	return 0;
}

C.P3582 KIN

思路
代码
#include<bits/stdc++.h>
#define N 1000005
#define ll long long
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
using namespace std;
inline ll read(){
    ll x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
struct film{ll sum,lx,rx,maxn;}tr[4*N];
ll f[N],w[N],res;
int head[N],pre[N];
film merge(film x,film y){
	return (film){x.sum+y.sum,max(x.lx,x.sum+y.lx),max(y.rx,y.sum+x.rx),max(max(x.maxn,y.maxn),x.rx+y.lx)};
}
void modify(int p,int l,int r,int pos,ll x){
	if(l==r){tr[p].sum=tr[p].lx=tr[p].rx=tr[p].maxn=x;return;}
	if(mid>=pos)modify(ls,l,mid,pos,x);
	else modify(rs,mid+1,r,pos,x);
	tr[p]=merge(tr[ls],tr[rs]);
}
int main(){
    ll n=read(),m=read();
    for(int i=1;i<=n;i++)f[i]=read();
    for(int i=1;i<=m;i++)w[i]=read();
    for(int i=1;i<=n;i++){
		pre[i]=head[f[i]];
		head[f[i]]=i;
		f[i]=w[f[i]];
    	modify(1,1,n,i,f[i]);
    	if(pre[i])modify(1,1,n,pre[i],-f[i]);
    	if(pre[pre[i]])modify(1,1,n,pre[pre[i]],0);
    	res=max(res,tr[1].maxn);
    }
    printf("%lld",res);
    return 0;
}

D.P2824 排序

思路
代码
#include<bits/stdc++.h>
#define N 100005
#define ll long long
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
using namespace std;
inline ll read(){
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
ll tr[N*4],a[N],n,m,L[N],R[N],op[N],lz[4*N],q;
void build(int p,int l,int r,int x){
    if(l==r){tr[p]=a[l]>=x;lz[p]=0;return;}
    build(ls,l,mid,x);build(rs,mid+1,r,x);
    tr[p]=tr[ls]+tr[rs];
    lz[p]=0;
}
void push_down(int p,int l,int r){
	if(!lz[p])return;
    lz[ls]=lz[rs]=lz[p];
    if(lz[p]==1){tr[ls]=mid-l+1;tr[rs]=r-mid;}
    else tr[ls]=tr[rs]=0;
    lz[p]=0;
}
void modify_area(int p,int l,int r,int ml,int mr,int x){
    if(l>=ml&&r<=mr){tr[p]=(r-l+1)*x;lz[p]=x?1:-1;return;}
    if(l>mr||r<ml)return;
    push_down(p,l,r);
    modify_area(ls,l,mid,ml,mr,x);
	modify_area(rs,mid+1,r,ml,mr,x);
    tr[p]=tr[ls]+tr[rs];
}
ll query(int p,int l,int r,int ql,int qr){
    if(l>qr||r<ql)return 0;
    if(l>=ql&&r<=qr)return tr[p];
    push_down(p,l,r);
    return query(ls,l,mid,ql,qr)+query(rs,mid+1,r,ql,qr);
}
ll ask(int p,int l,int r,int pos){
    if(l==pos&&pos==r)return tr[p];
    push_down(p,l,r);
    if(pos<=mid)return ask(ls,l,mid,pos);
    else return ask(rs,mid+1,r,pos);
}
bool check(ll x){
    build(1,1,n,x);
    for(int i=1;i<=m;i++){
        ll cnt=query(1,1,n,L[i],R[i]);
        if(!op[i]){
            modify_area(1,1,n,L[i],R[i]-cnt,0);
            modify_area(1,1,n,R[i]-cnt+1,R[i],1);
        }else{
            modify_area(1,1,n,L[i],L[i]+cnt-1,1);
            modify_area(1,1,n,L[i]+cnt,R[i],0);
        }
    }
    return ask(1,1,n,q);
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=m;i++){op[i]=read(),L[i]=read(),R[i]=read();}
    q=read();
    ll l=1,r=n,ans=0;
    while(l<=r){
        if(check(mid)){ans=mid;l=mid+1;}
        else r=mid-1;
    }
    printf("%lld",ans);
    return 0;
}

高阶练习(尚未完成)

A.P3588 PUS

思路
代码

B.P5025 炸弹

思路
代码

后面的话

目前线段树算法还处于入门状态,其它的如主席树、线段树分治等高阶线段树结构及算法还需要深入学习

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值