给定一个长的数组
,有以下四种操作:
1 x k 把修改成
2 k 将数组顺序分为若干个长的段,反转每一段的元素
3 k 将数组顺序分为若干个长的段,交换第1与第2段,交换第3与第四段.........
4 l r 输出区间的元素和
操作1,4是相对容易的,仅需要一些数据结构维护,线段树,树状数组等。
注意到数组长,意味着每次二分的区间长度都一样,也就是在对应的线段树是一颗满二叉树,然后有注意到,操作2,3修改的区间恰恰在线段树上与某个结点对应(对应的意思是这个区间刚好和某个节点的管理区间相等)。
然后看了答案,反转某一区间,就是把这个区间对应的子树反转(交换这个子树的所有左右儿子),交换相邻区间,就是把管理这两个区间的区间的左右儿子交换,叶子层管理的元素个数是1,每往上一层这个数,假设这个线段树有一个从叶子往根的深度(叶子节点深度是0),那么这个节点管理的元素个数就是
,所以:
操作2就是把所有以深度为的结点为根的子树的所有左右儿子反转
操作3就是把所有深度为的结点的左右儿子反转
所以这棵线段树的左右儿子就不一定是和
,我们需要用数组记录左右儿子,一开始是
和
,交换
结点的左右儿子只需要
其次,对于线段树的懒标记,保存的地方应该是最不用下传的位置,那就是一起保存在根节点,然后查询的时候下传。对于基础线段树,加法标记还需要有的一个信息是“这个标记该到哪里去?”,这是不容易维护的,所以我们加减标记需要下传到管理这个区间的子区间的结点。因此,当维护的东西可以保存位置信息时,是可以保存在根节点,查询再下传的,对于这题,位置信息很明确,并且维护的是需要交换儿子和不需要交换儿子,那么我们可以状态压缩每个标记,记录两个标记,tag_rev和tag_swap,1左移位的位置是一代表深度
的结点需要交换儿子(这里是保存在
位,也可以保存在
位),同时注意到交换再交换等于不交换,所以更新标记的时候应该用异或操作。
push_down:
tag_rev:
如果现在结点需要交换左右儿子:时我们需要交换左右儿子,并且这个时反转标记,现在交换完还需要让两个儿子再交换,所以交换完后把自己的tag上的儿子那个深度的再异或一次再下传给儿子,也可以下传后再在儿子那个位置打上标记
tag_swap:
操作同上,只不过这个只需要交换当前层的左右儿子,不需要给儿子多打一次标记
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll read()
{
ll ret=0,base=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') base=-1;
ch=getchar();
}
while(isdigit(ch))
{
ret=(ret<<3)+(ret<<1)+ch-48;
ch=getchar();
}
return ret*base;
}
int n,m,a[300005],tag_rev[4000005],tag_swap[4000005];
int ls[4000005],rs[40000005],depth[4000005];
ll tree[4000005];
void push_up(int x){tree[x]=tree[ls[x]]+tree[rs[x]];}
void build(int l,int r,int x)
{
ls[x]=x<<1;rs[x]=x<<1|1;
if(l==r)
{
tree[x]=a[l];
return;
}
int mid=l+r>>1;
build(l,mid,ls[x]);
build(mid+1,r,rs[x]);
push_up(x);depth[x]=depth[ls[x]]+1;
}
void push_down(int x)
{
if(tag_rev[x])
{
if(tag_rev[x]&(1<<depth[x]))
{
swap(ls[x],rs[x]);
tag_rev[x]^=1<<depth[x]-1;
}
tag_rev[ls[x]]^=tag_rev[x];
tag_rev[rs[x]]^=tag_rev[x];
tag_rev[x]=0;
}
if(tag_swap[x])
{
if(tag_swap[x]&(1<<depth[x])) swap(ls[x],rs[x]);
tag_swap[ls[x]]^=tag_swap[x];
tag_swap[rs[x]]^=tag_swap[x];
tag_swap[x]=0;
}
}
void update(int nl,int nr,int l,int r,int x,ll k)
{
if(nl<=l&&r<=nr)
{
tree[x]=k;//单点修改不用乘区间长
return;
}
int mid=l+r>>1;push_down(x);
if(nl<=mid) update(nl,nr,l,mid,ls[x],k);
if(nr>mid) update(nl,nr,mid+1,r,rs[x],k);
push_up(x);
}
ll query(int nl,int nr,int l,int r,int x)
{
if(nl<=l&&r<=nr) return tree[x];
int mid=l+r>>1;push_down(x);ll ret=0;
if(nl<=mid) ret+=query(nl,nr,l,mid,ls[x]);
if(nr>mid) ret+=query(nl,nr,mid+1,r,rs[x]);
return ret;
}
int main()
{
n=1<<read();int m=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,n,1);
while(m--)
{
int op=read();
if(op==1)
{
int x=read();
update(x,x,1,n,1,read());
}
else if(op==2) tag_rev[1]^=1<<read();
else if(op==3) tag_swap[1]^=1<<read()+1;
else
{
int l=read(),r=read();
printf("%lld\n",query(l,r,1,n,1));
}
}
return 0;
}