You are given an array aa of length 2n2n. You should process qq queries on it. Each query has one of the following 44 types:
- Replace(x,k)Replace(x,k) — change axax to kk;
- Reverse(k)Reverse(k) — reverse each subarray [(i−1)⋅2k+1,i⋅2k][(i−1)⋅2k+1,i⋅2k] for all ii (i≥1i≥1);
- Swap(k)Swap(k) — swap subarrays [(2i−2)⋅2k+1,(2i−1)⋅2k][(2i−2)⋅2k+1,(2i−1)⋅2k] and [(2i−1)⋅2k+1,2i⋅2k][(2i−1)⋅2k+1,2i⋅2k] for all ii (i≥1i≥1);
- Sum(l,r)Sum(l,r) — print the sum of the elements of subarray [l,r][l,r].
Write a program that can quickly process given queries.
Input
The first line contains two integers nn, qq (0≤n≤180≤n≤18; 1≤q≤1051≤q≤105) — the length of array aa and the number of queries.
The second line contains 2n2n integers a1,a2,…,a2na1,a2,…,a2n (0≤ai≤1090≤ai≤109).
Next qq lines contains queries — one per line. Each query has one of 44 types:
- "11 xx kk" (1≤x≤2n1≤x≤2n; 0≤k≤1090≤k≤109) — Replace(x,k)Replace(x,k);
- "22 kk" (0≤k≤n0≤k≤n) — Reverse(k)Reverse(k);
- "33 kk" (0≤k<n0≤k<n) — Swap(k)Swap(k);
- "44 ll rr" (1≤l≤r≤2n1≤l≤r≤2n) — Sum(l,r)Sum(l,r).
It is guaranteed that there is at least one SumSum query.
Output
Print the answer for each SumSum query.
Examples
input
Copy
2 3 7 4 9 9 1 2 8 3 1 4 2 4
output
Copy
24
input
Copy
3 8 7 0 8 8 7 1 5 2 4 3 7 2 1 3 2 4 1 6 2 3 1 5 16 4 8 8 3 0
output
Copy
29 22 1
Note
In the first sample, initially, the array aa is equal to {7,4,9,9}{7,4,9,9}.
After processing the first query. the array aa becomes {7,8,9,9}{7,8,9,9}.
After processing the second query, the array aiai becomes {9,9,7,8}{9,9,7,8}
Therefore, the answer to the third query is 9+7+8=249+7+8=24.
In the second sample, initially, the array aa is equal to {7,0,8,8,7,1,5,2}{7,0,8,8,7,1,5,2}. What happens next is:
- Sum(3,7)Sum(3,7) →→ 8+8+7+1+5=298+8+7+1+5=29;
- Reverse(1)Reverse(1) →→ {0,7,8,8,1,7,2,5}{0,7,8,8,1,7,2,5};
- Swap(2)Swap(2) →→ {1,7,2,5,0,7,8,8}{1,7,2,5,0,7,8,8};
- Sum(1,6)Sum(1,6) →→ 1+7+2+5+0+7=221+7+2+5+0+7=22;
- Reverse(3)Reverse(3) →→ {8,8,7,0,5,2,7,1}{8,8,7,0,5,2,7,1};
- Replace(5,16)Replace(5,16) →→ {8,8,7,0,16,2,7,1}{8,8,7,0,16,2,7,1};
- Sum(8,8)Sum(8,8) →→ 11;
- Swap(0)Swap(0) →→ {8,8,0,7,2,16,1,7}{8,8,0,7,2,16,1,7}
题解:操作1:单点修改
操作2:将区间按2^k分为若干块,并将每一块中的元素倒置
操作3:将区间按2^k分为若干块,相邻的两块元素交换,每块的相对顺序不变
操作4:区间查询
由于给出的元素是2^n次方个,我们可以发现,对于某个k,实际上是对线段树上某一层的所有节点进行操作。我们将最下面一层设为第0层,则最上面一层为第n层。
先讨论Swap操作。 当我们Swap(2)时,实际上是将第2层的节点交换位置。我们用rev(k)表示第k层是否交换。
对于Reverse操作,我们将0~k层进行交换,因为倒置后每一层的相对顺序都发生改变。
对于修改和查询操作,
实际上,我们并不需要真的交换节点(肯定会TLE),我们只需要通过rev(k)来决定我们的遍历方向。
设add=当前节点线段长度/2
当rev(k)=1,即第k层需要交换时,我们将查询区间改变为另一边。
例如,当n=3, 即线段树范围为[1,8]时,查询[1,6]。
当rev(n)=0时,查询区间应分别为[1,4],[5,6]。因此,交换后的区间应为[1+add,4+add]和[5-add,6-add],即[5,8],[1,2]
对于rev(n)可以直接O(n)维护,不用放在线段树上维护(会TLE)
(图源https://www.cnblogs.com/syksykCCC/p/CF1401F.html)
代码:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<math.h>
using namespace std;
const int maxn=300000;
long long tree[maxn*10][5];
int lazy[maxn*10];
long long a[maxn];
void build(int x,int dep)
{
int l,r,mid;
l=tree[x][0];r=tree[x][1];
tree[x][4]=dep;
if (l==r)
{
tree[x][2]=a[l];
return;
}
mid=(l+r)/2;
tree[x+x][0]=l;tree[x+x][1]=mid;
tree[x+x+1][0]=mid+1;tree[x+x+1][1]=r;
build(x+x,dep-1);
build(x+x+1,dep-1);
tree[x][2]=tree[x+x][2]+tree[x+x+1][2];
}
void change(int x,int y,long long z)
{
tree[x][3]=lazy[tree[x][4]];
if ((tree[x][0]==y)&&(tree[x][1]==y))
{
tree[x][2]=z;
return;
}
long long mid=(tree[x][0]+tree[x][1])/2;
long long add=(tree[x][1]-tree[x][0]+1)/2;
if (tree[x][3]==0)
{
if (y<=mid) change(x+x,y,z);
else change(x+x+1,y,z);
}
else if (tree[x][3]==1)
{
if (y<=mid) change(x+x+1,y+add,z);
else change(x+x,y-add,z);
}
tree[x][2]=tree[x+x][2]+tree[x+x+1][2];
}
long long get(int x,int l,int r)
{
tree[x][3]=lazy[tree[x][4]];
if ((tree[x][0]==l)&&(tree[x][1]==r))
{
return (tree[x][2]);
}
int mid=(tree[x][0]+tree[x][1])/2;
int add=(tree[x][1]-tree[x][0]+1)/2;
if (tree[x][3]==0)
{
if (r<=mid) return get(x+x,l,r);
else if (l>mid) return get(x+x+1,l,r);
else return(get(x+x,l,mid)+get(x+x+1,mid+1,r));
}
else if (tree[x][3]==1)
{
if (r<=mid) return get(x+x+1,l+add,r+add);
else if (l>mid) return get(x+x,l-add,r-add);
else return(get(x+x,mid+1-add,r-add)+get(x+x+1,l+add,mid+add));
}
}
int main()
{
// freopen("data.in","r",stdin); //输入重定向,输入数据将从in.txt文件中读取
// freopen("a.txt","w",stdout); //输出重定向,输出数据将保存out.txt文件中
long long n,k,q;
cin>>n>>q;
long long sum=pow(2,n);
for (int i=1;i<=sum;i++)
cin>>a[i];
tree[1][0]=1;tree[1][1]=sum;
build(1,n);
for (int i=1;i<=q;i++)
{
long long x1,y1,z1;
cin>>x1;
if (x1==1)
{
cin>>y1>>z1;
change(1,y1,z1);
}
if (x1==2)
{
cin>>y1;
for (int j=1;j<=y1;j++)
lazy[j]=1-lazy[j];
}
if (x1==3)
{
cin>>y1;
lazy[y1+1]=1-lazy[y1+1];
}
if (x1==4)
{
cin>>y1>>z1;
cout<<get(1,y1,z1)<<endl;
}
}
//fclose(stdin);//关闭重定向输入
return 0;
}