终于完成了线段树的一些基础部分
总结一下吧
线段树基础
基本原理
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为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 炸弹
思路
代码
后面的话
目前线段树算法还处于入门状态,其它的如主席树、线段树分治等高阶线段树结构及算法还需要深入学习