首页 > 其他分享 >【CF1401F】 Reverse and Swap(层标记线段树)

【CF1401F】 Reverse and Swap(层标记线段树)

时间:2022-10-19 16:48:32浏览次数:72  
标签:CF1401F Reverse cdot 线段 2i leq cdot2 Swap 节点

原题链接

题意

给定一个长度为 \(2^n\) 的数组 \(a\),现在需要处理 \(q\) 个询问,每个询问是以下 \(4\) 种类型之一:

  1. \(Replace(x, k)\) 把 \(a_x\) 修改为 \(k\)。

  2. \(Reverse(k)\) 对于每一个 \(i(i\ge 1)\) ,把区间 \([(i-1)\cdot 2^k+1, i\cdot 2^k]\) 的元素翻转。

  3. \(Swap(k)\) 对于每一个 \(i(i\ge 1)\) ,交换区间 \([(2i-2)\cdot2^k+1,(2i-1)\cdot2^k]\) 和 \([(2i-1)\cdot2^k+1,2i\cdot2^k]\) 的所有元素。

  4. \(Sum(l,r)\) 输出区间 \([l,r]\) 中所有元素的和。

数据范围

\(0\leq n \leq 18,1\leq n \leq 10^5,0\leq a_i\leq10^9\)。

思路

注意到数组的长度和操作的区间比较特殊。不难发现建立线段树以后,每次 \(2,3\) 操作的区间必然对应到线段树上相应的若干完整的节点。

设线段树的根节点为第 \(n\) 层,层数依次往下递减,那么第 \(k\) 层自左向右第 \(i\) 个节点就对应到原数组的区间 \([(i-1)\cdot 2^k+1, i\cdot 2^k]\)。记 \(rev_i\) 表示第 \(i\) 层节点的左右儿子是否交换。

对于 Replace 和 Sum 操作,直接在线段树上递归修改或查询即可。

对于 Reverse 操作,在线段树上对应到将第 \(k\) 层的所有节点对应的区间翻转,实质上就是交换第 \(1 \sum k\) 层的所有节点的左右儿子,那么只需要将对应的 \(rev_i\) 修改即可。

对于 Swap 操作,不难发现就是交换第 \(k+1\) 层所有节点的左右儿子,只需要修改 \(rev_{k+1}\) 即可。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e5+10;
#define LL long long
int n,m;bool rev[N];LL val[N<<2];
void push_up(int p){val[p]=val[p<<1]+val[p<<1|1];}
void build(int p,int l,int r)
{
	if(l==r) return scanf("%lld",&val[p]),void();int mid=l+r>>1;
	build(p<<1,l,mid),build(p<<1|1,mid+1,r);push_up(p);
}
void update(int p,int l,int r,int x,int k,int dep)
{
	if(l==r) return val[p]=k,void();int mid=l+r>>1;
	if(x<=mid) update(p<<1|rev[dep],l,mid,x,k,dep-1);
	else update(p<<1|rev[dep]^1,mid+1,r,x,k,dep-1);
	push_up(p);
}
LL query(int p,int l,int r,int L,int R,int dep)
{
	if(L<=l&&r<=R) return val[p];int mid=l+r>>1;LL res=0;
	if(L<=mid) res+=query(p<<1|rev[dep],l,mid,L,R,dep-1);
	if(R>mid) res+=query(p<<1|rev[dep]^1,mid+1,r,L,R,dep-1);
	return res;
}
void Reverse(int k){for(int i=0;i<=k;i++) rev[i]^=1;}
int main()
{
	scanf("%d%d",&n,&m);build(1,1,1<<n);
	while(m--)
	{
		int opt,x,y;scanf("%d",&opt);
		if(opt==1) scanf("%d%d",&x,&y),update(1,1,1<<n,x,y,n);
		if(opt==2) scanf("%d",&x),Reverse(x);
		if(opt==3) scanf("%d",&x),rev[x+1]^=1;
		if(opt==4) scanf("%d%d",&x,&y),printf("%lld\n",query(1,1,1<<n,x,y,n));
	}
	return 0;
}

标签:CF1401F,Reverse,cdot,线段,2i,leq,cdot2,Swap,节点
From: https://www.cnblogs.com/NLCAKIOI/p/16806827.html

相关文章

  • 交换分区(swap概念)
    什么是Linux交换(swap)原创 sharplee 大乐学IT 2022-04-2422:10收录于合集#linux66个Linux内核将RAM分成内存块和交换(Swap)进程,交换(Swap)进程是当Linux内核......
  • [Typescript] 55. Medium - Reverse
    Implementthetypeversionof Array.reverseForexample:typea=Reverse<['a','b']>//['b','a']typeb=Reverse<['a','b','c']>//['c','b',......
  • Python reversed函数及用法
    eserved()是Pyton内置函数之一,其功能是对于给定的序列(包括列表、元组、字符串以及range(n)区间),该函数可以返回一个逆序序列的迭代器(用于遍历该逆序序列)。reserved()......
  • AcCoders 10692:【2022NOIP联测10 10月17日】交换(swap) 题解
    考虑把一次交换产生的贡献记录在交换的两个数字中较小的那个数字上。则构造一个好的序列的过程可以看成是:按照从小到大的顺序枚举每个数,每次选择将这个数放在序列的左边或......
  • 冲销已过账外向交货单BAPI:WS_REVERSE_GOODS_ISSUE
    前台操作:VL09填写装运点和交货单点击定义日期,将输入的实际过账日期输入到本地日期中。点勾然后点击冲销点击绿色勾,冲销成功或错误,则均会出现如果对话框。......
  • DEMO:冲销交货单过账凭证WS_REVERSE_GOODS_ISSUE
    reportzdemo_vl09.parametersp_vbelntypevbeln_vl.data:lt_likptypetableoflikp.data:ls_likplikelineoflt_likp.data:lt_mesg......
  • 条款25:考虑写一个不抛异常的swap函数
     注意:C++11后的std::swap模板函数,使用了移动构造函数和移动赋值函数。所以。对于pimpl手法的内置类型,有移动构造函数和移动赋值函数应该不用写std::swap的特化,当然写了......
  • ubuntu删除和增加swap分区
    1、安装gparted工具,然后打开gparted,添加磁盘,然后进行新建,  ,  、  ,  、  、  、 然后启用交换空间: ......
  • Minimum Swaps To Make Sequences Increasing
    MinimumSwapsToMakeSequencesIncreasingYouaregiventwointegerarraysofthesamelength nums1 and nums2 .Inoneoperation,youareallowedtoswap......
  • @linux 扩容|缩容 (增加与减小swap分区)
    文章目录​​swap分区增加​​​​1.swap的概述​​​​2.swap增加​​​​1)查看当前linux主机swap​​​​2)增加swap分区​​​​3)swap永久设定​​​​4)修改swap空间的swa......