CodeForces 1041F Reverse and Swap (线段树)

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:

  1. Replace(x,k)Replace(x,k) — change axax to kk;
  2. 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);
  3. 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);
  4. 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:

  1. Sum(3,7)Sum(3,7) →→ 8+8+7+1+5=298+8+7+1+5=29;
  2. Reverse(1)Reverse(1) →→ {0,7,8,8,1,7,2,5}{0,7,8,8,1,7,2,5};
  3. Swap(2)Swap(2) →→ {1,7,2,5,0,7,8,8}{1,7,2,5,0,7,8,8};
  4. Sum(1,6)Sum(1,6) →→ 1+7+2+5+0+7=221+7+2+5+0+7=22;
  5. Reverse(3)Reverse(3) →→ {8,8,7,0,5,2,7,1}{8,8,7,0,5,2,7,1};
  6. Replace(5,16)Replace(5,16) →→ {8,8,7,0,16,2,7,1}{8,8,7,0,16,2,7,1};
  7. Sum(8,8)Sum(8,8) →→ 11;
  8. 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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值