赛中写了一个复杂度教高的写法,在数据随机的情况下大概是一秒左右起伏,当时写了一个暴力对拍,拍了十分钟随机数据都可以过,结果比赛过程中我也不知道多会改的题面说不可以用lld要用I64d就很无语。。。
这个做法是参考18年wannfly-camp中提到过的区间与,区间或,区间最大值的写法,可以看一下我这篇:区间或,区间与,区间最大值
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
ll sum[maxn<<2|1];
ll lazy[maxn<<2|1];
int is[maxn<<2|1][39];
int is01[maxn<<2|1][39];
void pushup(int k){
sum[k]=sum[k<<1]+sum[k<<1|1];
for(int i=0;i<=20;++i){
if(is01[k<<1][i]==is01[k<<1|1][i]) is01[k][i]=is01[k<<1][i];
else is01[k][i]=2;
}
}
void pushup2(int k,int pos){
sum[k]=sum[k<<1]+sum[k<<1|1];
if(is01[k<<1][pos]==is01[k<<1|1][pos]) is01[k][pos]=is01[k<<1][pos];
else is01[k][pos]=2;
}
void build(int l,int r,int k){
lazy[k]=0;
for(int i=0;i<=20;++i) is[k][i]=-1;
if(l==r){
scanf("%lld",&sum[k]);
for(int i=0;i<=20;++i)
if(sum[k]&(1<<i)) is01[k][i]=1;
return ;
}
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
pushup(k);
}
void pushdown(int l,int r,int k){
if(lazy[k]){
int mid=(l+r)>>1;
lazy[k<<1]+=lazy[k];
lazy[k<<1|1]+=lazy[k];
sum[k<<1]+=(mid-l+1)*lazy[k];
sum[k<<1|1]+=(r-mid)*lazy[k];
lazy[k]=0;
}
for(int i=0;i<=30;++i)
if(is[k][i]!=-1){
is[k<<1][i]=is[k][i];
is[k<<1|1][i]=is[k][i];
is01[k<<1][i]=is[k][i];
is01[k<<1|1][i]=is[k][i];
is[k][i]=-1;
}
}
void pushdown2(int l,int r,int k,int id){
if(lazy[k]){
int mid=(l+r)>>1;
lazy[k<<1]+=lazy[k];
lazy[k<<1|1]+=lazy[k];
sum[k<<1]+=(mid-l+1)*lazy[k];
sum[k<<1|1]+=(r-mid)*lazy[k];
lazy[k]=0;
}
if(is[k][id]!=-1){
is[k<<1][id]=is[k][id];
is[k<<1|1][id]=is[k][id];
is01[k<<1][id]=is[k][id];
is01[k<<1|1][id]=is[k][id];
is[k][id]=-1;
}
}
//1& 2| 3^ num 01
void updata(int l,int r,int k,int L,int R,int pos,int type,int num){
if(l==r){
if(type==1){
if(is01[k][pos]&&num==0) sum[k]-=(1<<pos),is01[k][pos]=0;
}
else if(type==2){
if(is01[k][pos]==0&&num) sum[k]+=(1<<pos),is01[k][pos]=1;
}
else{
if(is01[k][pos]&&num) sum[k]-=(1<<pos),is01[k][pos]=0;
else if(is01[k][pos]==0&&num) sum[k]+=(1<<pos),is01[k][pos]=1;
}
return ;
}
if(l>=L&&r<=R){
if(type==1){
if(is01[k][pos]==1&&num==0){
sum[k]-=(r-l+1)*(1<<pos),is01[k][pos]=0;
lazy[k]-=(1<<pos);
is[k][pos]=0;
}
}
else if(type==2){
if(is01[k][pos]==0&&num){
sum[k]+=(r-l+1)*(1<<pos),is01[k][pos]=1;
lazy[k]+=(1<<pos);
is[k][pos]=1;
}
}
else{
if(is01[k][pos]==1&&num){
sum[k]-=(r-l+1)*(1<<pos),is01[k][pos]=0;
lazy[k]-=(1<<pos);
is[k][pos]=0;
}
else if(is01[k][pos]==0&&num){
sum[k]+=(r-l+1)*(1<<pos),is01[k][pos]=1;
lazy[k]+=(1<<pos);
is[k][pos]=1;
}
}
if(is01[k][pos]!=2) return ;
}
//pushdown(k,pos);
pushdown2(l,r,k,pos);
int mid=(l+r)>>1;
if(L<=mid) updata(l,mid,k<<1,L,R,pos,type,num);
if(R>mid) updata(mid+1,r,k<<1|1,L,R,pos,type,num);
pushup2(k,pos);
}
ll myfind(int l,int r,int k,int L,int R){
if(l>=L&&r<=R) return sum[k];
pushdown(l,r,k);
//for(int i=0;i<=30;++i) pushdown(k,i);
int mid=(l+r)>>1;
ll res=0;
if(L<=mid) res+=myfind(l,mid,k<<1,L,R);
if(R>mid) res+=myfind(mid+1,r,k<<1|1,L,R);
pushup(k);
return res;
}
int main(){
int n,m;
int id,l,r,x;
scanf("%d",&n);
build(1,n,1);
scanf("%d",&m);
while(m--){
scanf("%d%d%d",&id,&l,&r);
if(id==1) printf("%I64d\n",myfind(1,n,1,l,r));
else if(id==4){//&;
scanf("%d",&x);
for(int i=0;i<=20;++i)
if(x>>i&1) updata(1,n,1,l,r,i,1,1);
else updata(1,n,1,l,r,i,1,0);
}
else if(id==3){//|;
scanf("%d",&x);
for(int i=0;i<=20;++i)
if(x>>i&1) updata(1,n,1,l,r,i,2,1);
else updata(1,n,1,l,r,i,2,0);
}
else{
scanf("%d",&x);
for(int i=0;i<=20;++i)
if(x>>i&1) updata(1,n,1,l,r,i,3,1);
else updata(1,n,1,l,r,i,3,0);
}
}
return 0;
}
另一种较为优秀的是下传两种标记,一种是区间赋值,一种是区间翻转,&|运算相当于赋值,^相当于翻转。
要注意两种标记的时间顺序,如果赋值之前有翻转标记,那么翻转标记直接无效。
如果翻转之前有赋值操作,那么其实可以看作是赋相反的值(0赋1,1赋0),翻转标记无效化。
所以在标记下传的过程中,存在区间翻转就不会存在区间赋值,存在区间赋值就不会存在区间翻转。
(最后这句话没用。)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
int sum[maxn<<2|1][29];
int revarse[maxn<<2|1][29];
int add[maxn<<2|1][29];
inline void pushup(int k,int pos){
sum[k][pos]=sum[k<<1][pos]+sum[k<<1|1][pos];
}
void build(int l,int r,int k){
for(int i=0;i<=24;++i) revarse[k][i]=0,add[k][i]=-1;
int x;
if(l==r){
scanf("%d",&x);
for(int i=0;i<=24;++i)
if(x&(1<<i)) sum[k][i]=1;
return ;
}
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
for(int i=0;i<=24;++i) pushup(k,i);
}
inline void pushdown(int l,int r,int k,int pos){
int mid=(l+r)>>1;
if(revarse[k][pos]){
revarse[k<<1][pos]^=revarse[k][pos];
revarse[k<<1|1][pos]^=revarse[k][pos];
sum[k<<1][pos]=mid-l+1-sum[k<<1][pos];
sum[k<<1|1][pos]=r-mid-sum[k<<1|1][pos];
if(add[k<<1][pos]!=-1){
add[k<<1][pos]^=1;
revarse[k<<1][pos]=0;
}
if(add[k<<1|1][pos]!=-1){
add[k<<1|1][pos]^=1;
revarse[k<<1|1][pos]=0;
}
revarse[k][pos]=0;
}
if(add[k][pos]!=-1){
add[k<<1][pos]=add[k<<1|1][pos]=add[k][pos];
sum[k<<1][pos]=(mid-l+1)*add[k][pos];
sum[k<<1|1][pos]=(r-mid)*add[k][pos];
revarse[k<<1][pos]=revarse[k<<1|1][pos]=0;
add[k][pos]=-1;
}
}
//0 0 1 1 2 revarse;
void updata(int l,int r,int k,int L,int R,int type,int pos){
if(l>=L&&r<=R){
if(type==0){
sum[k][pos]=0;
add[k][pos]=0;
revarse[k][pos]=0;
}
else if(type==1){
sum[k][pos]=r-l+1;
add[k][pos]=1;
revarse[k][pos]=0;
}
else{
revarse[k][pos]^=1;//如果之前有覆盖标记,那么就相当于覆盖为相反的数字,从而达到反转效果;
sum[k][pos]=r-l+1-sum[k][pos];
if(add[k][pos]!=-1){
add[k][pos]^=1;
revarse[k][pos]=0;
}
}
return ;
}
pushdown(l,r,k,pos);
int mid=(l+r)>>1;
if(L<=mid) updata(l,mid,k<<1,L,R,type,pos);
if(R>mid) updata(mid+1,r,k<<1|1,L,R,type,pos);
pushup(k,pos);
}
int myfind(int l,int r,int k,int L,int R,int pos){
if(l>=L&&r<=R) return sum[k][pos];
int mid=(l+r)>>1;
pushdown(l,r,k,pos);
int res=0;
if(L<=mid) res+=myfind(l,mid,k<<1,L,R,pos);
if(R>mid) res+=myfind(mid+1,r,k<<1|1,L,R,pos);
return res;
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out1.txt","w",stdout);
int n,m,id,l,r,x;
scanf("%d",&n);
build(1,n,1);
scanf("%d",&m);
while(m--){
scanf("%d%d%d",&id,&l,&r);
if(id==1){
ll res=0;
for(int i=0;i<=24;++i) res+=(1LL<<i)*1LL*myfind(1,n,1,l,r,i);
printf("%I64d\n",res);
}
else if(id==2){
scanf("%d",&x);
for(int i=0;i<=24;++i)
if(x&(1<<i)) updata(1,n,1,l,r,2,i);
}
else if(id==3){
scanf("%d",&x);
for(int i=0;i<=24;++i)
if(x&(1<<i)) updata(1,n,1,l,r,1,i);
}
else{
scanf("%d",&x);
for(int i=0;i<=24;++i)
if(!(x&(1<<i))) updata(1,n,1,l,r,0,i);
}
}
return 0;
}
本文详细介绍了在数据结构中如何高效地进行区间操作与查询,包括区间与、区间或、区间最大值等复杂操作的实现。通过两种不同的标记传递策略,展示了如何在保持树状结构的同时,快速响应大规模数据更新和查询请求。
1031

被折叠的 条评论
为什么被折叠?



