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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值