这两天可算是把线段树的题刷完了,感觉线段树更熟练了(😓
A 【例题1】求区间和
板子题,甚至不涉及到pushdown,直接修改即可
Code
#include<bits/stdc++.h>
#define re register
#define int long long
#define inl inline
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return f*sum;
}
int n,m;
int tree[400010];
inl void add(int k,int l,int r,int x,int y){
if(l==r){
if(l==r) tree[k]+=y;
return;
}
int mid=l+r>>1;
if(x<=mid) add(k<<1,l,mid,x,y);
else add(k<<1|1,mid+1,r,x,y);
tree[k]=tree[k<<1]+tree[k<<1|1];
}
inl int query(int k,int l,int r,int x,int y){
if(l>=x&&r<=y) return tree[k];
if(r<x||l>y) return 0;
int mid=l+r>>1;
int ans=0;
if(x<=mid) ans+=query(k<<1,l,mid,x,y);
if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y);
return ans;
}
signed main(){
n=read(),m=read();
for(re int i=1;i<=m;i++){
int opt=read(),x=read(),y=read();
if(opt==0) add(1,1,n,x,y);
else printf("%lld\n",query(1,1,n,x,y));
}
return 0;
}
B. 【例题2】区间查改
板子plus 对于这里不用一个一个的去修改,,如果一个区间在修改范围内,不妨将直接将其区间总和加上
v
×
(
r
−
l
+
1
)
v\times (r-l+1)
v×(r−l+1) 同时记录一个懒标记,如果之后修改的区间在其之内就将其下放,大大节约修改时间
Code
#include<bits/stdc++.h>
#define re register
#define inl inline
#define int long long
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return f*sum;
}
const int N=1e6+10;
struct node{
int l,r,sum;
}tree[N<<2];
int n,m,a[N],add[N<<2];
inl void build(int k,int l,int r){
tree[k].l=l,tree[k].r=r;
if(l==r){
tree[k].sum=a[l];
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
inl void update(int k,int l,int r,int s){
add[k]+=s;
tree[k].sum+=(r-l+1)*s;
}
void pushdown(int k,int l,int r){
if(!add[k]) return;
int mid=l+r>>1;
update(k<<1,l,mid,add[k]);
update(k<<1|1,mid+1,r,add[k]);
add[k]=0;
}
inl void Add(int k,int l,int r,int x,int y,int v){
if(l>y||r<x) return;
if(l>=x&&y>=r){
add[k]+=v;
tree[k].sum+=(r-l+1)*v;
return;
}
pushdown(k,l,r);
int mid=l+r>>1;
if(x<=mid) Add(k<<1,l,mid,x,y,v);
if(y>mid) Add(k<<1|1,mid+1,r,x,y,v);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
int query(int k,int l,int r,int x,int y){
if(l>y||r<x) return 0;
if(x<=l&&r<=y) return tree[k].sum;
pushdown(k,l,r);
int mid=l+r>>1;
int ans=0;
if(x<=mid) ans+=query(k<<1,l,mid,x,y);
if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y);
return ans;
}
signed main(){
n=read(),m=read();
for(re int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
for(re int i=1;i<=m;i++){
int opt=read();
if(opt==1){
int x=read(),y=read(),v=read();
Add(1,1,n,x,y,v);
}
else {
int x=read(),y=read();
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
C. 【例题3】公园遛狗
思路 计算区间最大连续子串和,我们对于一颗树需要维护有三个变量:区间最大子串和、从左开始的最大子串和、从右端结尾的最大子串和
为什么这么维护?
当我们合并一棵树时,他的区间最大值,有三种情况,一种是左区间的最大值,一种是右区间的最大值,另一种包含了连个区间,而最后一种的组成是由左子树的右端结尾的最大子串和与右子树左端开始的最大子串和组成的
合并方程式:
t
r
e
e
[
k
]
.
m
a
x
n
=
m
a
x
(
t
r
e
e
[
k
<
<
1
]
.
m
a
x
n
,
t
r
e
e
[
k
<
<
1
∣
1
]
.
m
a
x
n
,
t
r
e
e
[
k
<
<
1
]
.
r
m
a
x
+
t
r
e
e
[
k
<
<
1
∣
1
]
.
l
m
a
x
)
tree[k].maxn=max(tree[k<<1].maxn,tree[k<<1|1].maxn,tree[k<<1].rmax+tree[k<<1|1].lmax)
tree[k].maxn=max(tree[k<<1].maxn,tree[k<<1∣1].maxn,tree[k<<1].rmax+tree[k<<1∣1].lmax)
t
r
e
e
[
k
]
.
l
m
a
x
=
m
a
x
(
t
r
e
e
[
k
<
<
1
]
.
l
m
a
x
,
t
r
e
e
[
k
<
<
1
]
.
s
u
m
+
t
r
e
e
[
k
<
<
1
∣
1
]
.
l
m
a
x
)
tree[k].lmax=max(tree[k<<1].lmax,tree[k<<1].sum+tree[k<<1|1].lmax)
tree[k].lmax=max(tree[k<<1].lmax,tree[k<<1].sum+tree[k<<1∣1].lmax)
t
r
e
e
[
k
]
.
r
m
a
x
=
m
a
x
(
t
r
e
e
[
k
<
<
1
∣
1
]
.
r
m
a
x
,
t
r
e
e
[
k
<
<
1
∣
1
]
.
s
u
m
+
t
r
e
e
[
k
<
<
1
]
.
r
m
a
x
)
tree[k].rmax=max(tree[k<<1|1].rmax,tree[k<<1|1].sum+tree[k<<1].rmax)
tree[k].rmax=max(tree[k<<1∣1].rmax,tree[k<<1∣1].sum+tree[k<<1].rmax)
t
r
e
e
[
k
]
.
s
u
m
=
t
r
e
e
[
k
<
<
1
]
.
s
u
m
+
t
r
e
e
[
k
<
<
1
∣
1
]
.
s
u
m
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum
tree[k].sum=tree[k<<1].sum+tree[k<<1∣1].sum
Code
#include<bits/stdc++.h>
#define re register
#define inl inline
#define int long long
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return f*sum;
}
const int N=5e5+10;
struct node{
int sum,maxx,lmax,rmax;
}tree[N<<2];
int a[N],n,m;
inl node unity(node x,node y){
node tmp;
tmp.sum=x.sum+y.sum;
tmp.lmax=max(x.lmax,x.sum+y.lmax);
tmp.rmax=max(y.rmax,x.rmax+y.sum);
tmp.maxx=max(max(x.maxx,y.maxx),x.rmax+y.lmax);
return tmp;
}
inl void build(int k,int l,int r){
if(l==r){tree[k].lmax=tree[k].maxx=tree[k].rmax=tree[k].sum=a[l];return;}
int mid=l+r>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
tree[k]=unity(tree[k<<1],tree[k<<1|1]);
return;
}
inl node ask(int k,int l,int r,int x,int y){
if(x<=l&&r<=y) return tree[k];
int mid=l+r>>1;
if(y<=mid) return ask(k<<1,l,mid,x,y);
if(x>mid) return ask(k<<1|1,mid+1,r,x,y);
node a,b,c;
a=ask(k<<1,l,mid,x,y);
b=ask(k<<1|1,mid+1,r,x,y);
c=unity(a,b);
return c;
}
inl void chenge(int k,int l,int r,int x,int y){
if(l==r){tree[k].lmax=tree[k].rmax=tree[k].maxx=tree[k].sum=y;return;}
int mid=l+r>>1;
if(x<=mid) chenge(k<<1,l,mid,x,y);
if(x>mid) chenge(k<<1|1,mid+1,r,x,y);
tree[k]=unity(tree[k<<1],tree[k<<1|1]);
}
signed main(){
n=read(),m=read();
for(re int i=1;i<=n;i++){
a[i]=read();
}
build(1,1,n);
while(m--){
int opt=read(),x=read(),y=read();
if(opt==2) chenge(1,1,n,x,y);
else{
if(x>y) swap(x,y);
node tmp=ask(1,1,n,x,y);
printf("%lld\n",tmp.maxx);
}
}
return 0;
}
D. 【例题4】维护序列
线段树区间乘裸题,要注意的就是设置两个懒标记,一个为mul,一个add,下放懒标记时先乘再加,进行乘法时,懒标记同时要乘,记得随时取模!!
Code
#include<bits/stdc++.h>
#define re register
#define inl inline
#define int long long
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return f*sum;
}
const int N=1e5+10;
struct node{
int sum,lazy,mul;
}tree[N<<2];
int n,m,a[N],mod;
inl void update(int k){
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
tree[k].sum%=mod;
}
inl void pushdown(int k,int l,int r){
if(tree[k].mul!=1){
tree[k<<1].mul*=tree[k].mul;tree[k<<1].mul%=mod;tree[k<<1|1].mul*=tree[k].mul;tree[k<<1|1].mul%=mod;
tree[k<<1].lazy*=tree[k].mul;tree[k<<1].lazy%=mod;tree[k<<1|1].lazy*=tree[k].mul;tree[k<<1|1].lazy%=mod;
tree[k<<1].sum*=tree[k].mul;tree[k<<1].sum%=mod;tree[k<<1|1].sum*=tree[k].mul;tree[k<<1|1].sum%=mod;
tree[k].mul=1;
}
int mid=l+r>>1;
if(tree[k].lazy){
tree[k<<1].sum+=tree[k].lazy*(mid-l+1);tree[k<<1].sum%=mod;
tree[k<<1|1].sum+=tree[k].lazy*(r-mid);tree[k<<1|1].sum%=mod;
tree[k<<1].lazy+=tree[k].lazy;tree[k<<1].lazy%=mod;
tree[k<<1|1].lazy+=tree[k].lazy;tree[k<<1|1].lazy%=mod;
tree[k].lazy=0;
}
return;
}
inl void build(int k,int l,int r){
tree[k].mul=1;
if(l==r){
tree[k].sum=a[l];
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(k);
}
inl void mymul(int k,int l,int r,int x,int y,int v){
if(x<=l&&r<=y){
tree[k].mul*=v;tree[k].mul%=mod;
tree[k].lazy*=v;tree[k].lazy%=mod;
tree[k].sum*=v;tree[k].sum%=mod;
return;
}
pushdown(k,l,r);
int mid=l+r>>1;
if(x<=mid) mymul(k<<1,l,mid,x,y,v);
if(y>mid) mymul(k<<1|1,mid+1,r,x,y,v);
update(k);
}
inl void add(int k,int l,int r,int x,int y,int v){
if(x<=l&&r<=y){
tree[k].sum+=(r-l+1)*v;
tree[k].sum%=mod;
tree[k].lazy+=v;
tree[k].lazy%=mod;
return;
}
pushdown(k,l,r);
int mid=l+r>>1;
if(x<=mid) add(k<<1,l,mid,x,y,v);
if(y>mid) add(k<<1|1,mid+1,r,x,y,v);
update(k);
}
inl int query(int k,int l,int r,int x,int y){
int ans=0;
if(x<=l&&r<=y) return tree[k].sum;
int mid=l+r>>1;
pushdown(k,l,r);
if(x<=mid) ans+=query(k<<1,l,mid,x,y);
ans%=mod;
if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y);
ans%=mod;
return ans;
}
signed main(){
n=read(),mod=read();
for(re int i=1;i<=n;i++) a[i]=read();
int m=read();
build(1,1,n);
for(re int i=1;i<=m;i++){
int opt=read();
if(opt==1){
int x=read(),y=read(),v=read();
mymul(1,1,n,x,y,v);
}
if(opt==2){
int x=read(),y=read(),v=read();
add(1,1,n,x,y,v);
}
if(opt==3){
int x=read(),y=read();
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
E. 【例题5】字符串排序
思路用线段树维护区间内a~z字母的数量,再排序(区间赋值)——>其实就是将
l
−
l
+
c
n
t
a
−
1
l-l+cnt_a-1
l−l+cnta−1赋为a,
l
+
c
n
t
a
−
l
+
c
n
t
a
+
c
n
t
b
−
1
l+cnt_a-l+cnt_a+cnt_b-1
l+cnta−l+cnta+cntb−1赋为b依次类推,至于降序就是反过来。
而维护a~z的数目不如开26棵线段树来维护即可
Code
#include<bits/stdc++.h>
#define re register
#define inl inline
#define ll long long
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return f*sum;
}
const int N=1e5+10;
int n,m,kk[30];
char s1[N<<2];
int tree[N<<2][30],lazy[N<<2];
inl void update(int k){
for(re int i=1;i<=26;i++){
tree[k][i]=tree[k<<1][i]+tree[k<<1|1][i];
}
}
inl void pushdown(int k,int l,int r){
if(!lazy[k]||l==r) return;
int mid=l+r>>1;
lazy[k<<1]=lazy[k<<1|1]=lazy[k];
for(re int i=1;i<=26;i++) tree[k<<1][i]=tree[k<<1|1][i]=0;
tree[k<<1][lazy[k]]=mid-l+1;
tree[k<<1|1][lazy[k]]=r-mid;
lazy[k]=0;
}
inl void build(int k,int l,int r){
if(l==r){
tree[k][s1[l]-'a'+1]=1;
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(k);
}
inl void change(int k,int l,int r,int x,int y,int v){
if(x>y) return;
if(x<=l&&r<=y){
lazy[k]=v;
for(re int i=1;i<=26;i++){
tree[k][i]=0;
}
tree[k][v]=r-l+1;
return;
}
int mid=l+r>>1;
pushdown(k,l,r);
if(x<=mid) change(k<<1,l,mid,x,y,v);
if(y>mid) change(k<<1|1,mid+1,r,x,y,v);
update(k);
}
int query(int k,int l,int r,int x,int y,int v){
if(x<=l&&r<=y) return tree[k][v];
int mid=l+r>>1,ans=0;
pushdown(k,l,r);
if(x<=mid) ans+=query(k<<1,l,mid,x,y,v);
if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y,v);
return ans;
}
inl void w(int k,int l,int r){
int mid=l+r>>1;
if(l==r){
for(re int i=1;i<=26;i++){
if(tree[k][i]){printf("%c",'a'+i-1);return;}
}
}
pushdown(k,l,r);
w(k<<1,l,mid);
w(k<<1|1,mid+1,r);
}
int main(){
n=read(),m=read();
scanf("%s",s1+1);
build(1,1,n);
while(m--){
int l=read(),r=read(),x=read();
for(re int i=1;i<=26;i++){
kk[i]=query(1,1,n,l,r,i);
}
if(x==1){
int now=l;
for(re int i=1;i<=26;i++){
if(kk[i]){
change(1,1,n,now,now+kk[i]-1,i);
now+=kk[i];
if(now>r) break;
}
}
}
else{
int now=l;
for(re int i=26;i>=1;i--){
if(kk[i]){
change(1,1,n,now,now+kk[i]-1,i);
now+=kk[i];
if(now>r) break;
}
}
}
}
w(1,1,n);
return 0;
}
F. 1.取模问题
其实就是将区间内大于x的数找出来进行取模即可,而找出大于x的数,不妨维护区间最大值,如果区间最大值小于x就不用继续修改了,反之一直找到
l
=
=
r
l==r
l==r为止然后进行修改
Code
#include<bits/stdc++.h>
#define re register
#define inl inline
#define int long long
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return f*sum;
}
const int N=1e5+10;
struct node{
int sum,maxn;
}tree[N<<2];
int a[N],n,m;
inl void update(int k){
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
tree[k].maxn=max(tree[k<<1].maxn,tree[k<<1|1].maxn);
}
inl void build(int k,int l,int r){
if(l==r){
tree[k].maxn=tree[k].sum=a[l];
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(k);
}
inl void mo(int k,int l,int r,int x,int y,int mod){
if(tree[k].maxn<mod) return;
if(l==r){
tree[k].maxn=tree[k].sum=tree[k].sum%mod;
return;
}
int mid=l+r>>1;
if(x<=mid) mo(k<<1,l,mid,x,y,mod);
if(y>mid) mo(k<<1|1,mid+1,r,x,y,mod);
update(k);
}
inl void change(int k,int l,int r,int pos,int y){
if(l==r){
tree[k].maxn=tree[k].sum=y;
return;
}
int mid=l+r>>1;
if(pos<=mid) change(k<<1,l,mid,pos,y);
else change(k<<1|1,mid+1,r,pos,y);
update(k);
}
inl int query(int k,int l,int r,int x,int y){
if(x<=l&&r<=y) return tree[k].sum;
int mid=l+r>>1;
int ans=0;
if(x<=mid) ans+=query(k<<1,l,mid,x,y);
if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y);
return ans;
}
signed main(){
n=read(),m=read();
for(re int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
while(m--){
int opt=read();
if(opt==1){
int x=read(),y=read();
printf("%lld\n",query(1,1,n,x,y));
}
if(opt==2){
int l=read(),r=read(),x=read();
mo(1,1,n,l,r,x);
}
if(opt==3){
int pos=read(),y=read();
change(1,1,n,pos,y);
}
}
return 0;
}
G. 2.魔法传输
这里有一个小技巧就是修改区间时,每个区间修改的值为
i
−
l
+
1
i-l+1
i−l+1,所以我们维护时还是维护两个值,一个是修改次数,另一个为
l
−
1
l-1
l−1,最终计算时,答案为
i
×
c
n
t
i
−
l
a
z
y
i
i\times cnt_i-lazy_i
i×cnti−lazyi
Code
#include<bits/stdc++.h>
#define re register
#define int long long
#define inl inline
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return sum*f;
}
const int mod=1e9+7,N=1e5+10;
int n,m;
struct node{
int cnt,lazy;
}tree[N<<2];
inl void pushdown(int k){
if(!tree[k].cnt) return;
tree[k<<1].lazy=(tree[k].lazy+tree[k<<1].lazy)%mod;
tree[k<<1|1].lazy=(tree[k<<1|1].lazy+tree[k].lazy)%mod;
tree[k<<1].cnt+=tree[k].cnt;tree[k<<1].cnt%=mod;
tree[k<<1|1].cnt+=tree[k].cnt;tree[k<<1|1].cnt%=mod;
tree[k].cnt=0;tree[k].lazy=0;
}
inl void add(int k,int l,int r,int x,int y){
if(x<=l&&r<=y){
tree[k].cnt=(tree[k].cnt+1)%mod;
tree[k].lazy=(tree[k].lazy+x-1)%mod;
return;
}
pushdown(k);
int mid=l+r>>1;
if(x<=mid) add(k<<1,l,mid,x,y);
if(y>mid) add(k<<1|1,mid+1,r,x,y);
}
inl int query(int k,int l,int r,int x){
if(l==r) return (tree[k].cnt*l%mod-tree[k].lazy%mod+mod)%mod;
int mid=l+r>>1;
pushdown(k);
if(x<=mid) return query(k<<1,l,mid,x);
else return query(k<<1|1,mid+1,r,x);
}
signed main(){
n=read(),m=read();
for(re int i=1;i<=m;i++){
char opt;
cin>>opt;
if(opt=='C'){
int l=read(),r=read();
add(1,1,n,l,r);
}
else{
int x=read();
printf("%lld\n",query(1,1,n,x));
}
}
return 0;
}
H. 3.队伍整理
思路:维护一个长度为n+m的线段树,访问挺简单,修改时,将那个位置的排名赋为1e9,再将其排名赋到结尾处,第一问就处理完了
对于第二问,其实就是要看在一个长度为n的区间内最多的学生数,用一个前缀和维护即可
注意建树要一步完成,我就是刚开始维护的区间长度为n,之后调动一个n++,使线段树紊乱,牢记
Code
#include<bits/stdc++.h>
#define re register
#define int long long
#define inl inline
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return sum*f;
}
const int N=2e5+10;
const int M=1e9;
struct node{
int minn;
}tree[N<<2];
int a[N<<2],b[10000010],n,m,sum[N<<2],ans;
bool vis[N<<2];
inl void update(int k){
tree[k].minn=min(tree[k<<1].minn,tree[k<<1|1].minn);
}
inl void build(int k,int l,int r){
if(l==r){
if(l>n) tree[k].minn=1e9;
else tree[k].minn=a[l];
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(k);
}
inl void change(int k,int l,int r,int x,int v){
if(l==r&&l==x){tree[k].minn=v;return;}
int mid=l+r>>1;
if(x<=mid) change(k<<1,l,mid,x,v);
else change(k<<1|1,mid+1,r,x,v);
update(k);
}
inl int query(int k,int l,int r,int x,int y){
if(y<x) return M;
if(x<=l&&r<=y) return tree[k].minn;
int mid=l+r>>1;
int ans=M;
if(x<=mid) ans=min(ans,query(k<<1,l,mid,x,y));
if(y>mid) ans=min(ans,query(k<<1|1,mid+1,r,x,y));
return ans;
}
int tmp;
inl void work(){
for(re int i=1;i<=n;i++) vis[b[a[i]]]=1;
for(re int i=1;i<=n+tmp;i++) sum[i]=sum[i-1]+vis[i];
for(re int i=n;i<=tmp+n;i++) ans=max(ans,sum[i]-sum[i-n]);
}
signed main(){
n=read(),m=read();
for(re int i=1;i<=n+m;i++) a[i]=M;
for(re int i=1;i<=n;i++) a[i]=read(),b[a[i]]=i;
build(1,1,n+m);
tmp=m;
int flag=n;
while(m--){
char opt;
cin>>opt;
if(opt=='A'){
int x=read();
if(!b[x]){puts("-1");continue;}
int res=query(1,1,n+tmp,1,b[x]-1);
if(res==M) res=-1;
printf("%lld\n",res);
}
else{
int x=read();
change(1,1,n+tmp,b[x],M);
flag++;
change(1,1,n+tmp,flag,x);
b[x]=flag;
}
}
work();
printf("%lld",n-ans);
return 0;
}
I. 4.和或异或
裸的线段树合并问题,但是要记录一下上一步的操作即可
Code
#include<bits/stdc++.h>
#define re register
#define int long long
#define inl inline
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return sum*f;
}
int n,q;
int a[(1<<17)+10],fa[((1<<17)+10)<<2];
int tree[((1<<17)+10)<<2];
inl void update(int k){
fa[k]=fa[k<<1]^1;
if(fa[k]) tree[k]=tree[k<<1]|tree[k<<1|1];
else tree[k]=tree[k<<1]^tree[k<<1|1];
}
inl void build(int k,int l,int r){
if(l==r){tree[k]=a[l];return;}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(k);
}
inl void change(int k,int l,int r,int pos,int x){
if(l==r){
tree[k]=x;
return;
}
int mid=l+r>>1;
if(pos<=mid) change(k<<1,l,mid,pos,x);
else change(k<<1|1,mid+1,r,pos,x);
update(k);
}
signed main(){
n=read();q=read();
int w=(1<<n);
for(re int i=1;i<=w;i++) a[i]=read();
build(1,1,w);
while(q--){
int pos=read(),y=read();
change(1,1,w,pos,y);
printf("%d\n",tree[1]);
}
return 0;
}
J. 5.括号匹配
维护一个区间内剩余左括弧与右括弧的个数,再进行合并即可(注意一个左括号只能配一个右括号!!!,而且这道题说的是子序列,不用想复杂,直接暴力线段树维护区间内括号数即可,另外,别忘记求的是长度,别忘乘2)
完结
这份题解完成后,我一直也不太愿碰的线段树的惰性心理终于消散了,终于不用看着就逃了(doge