Codeforces Round #665 (Div. 2) F.Reverse and Swap(线段树 区间翻转 子树交换)

题目

长度为2^{n}的数组(n<=18)a[],保证任意时刻ai<=1e9,

维护q(q<=1e5)次操作,操作分四种,

1 x k 把ax替换为k

2 k 对[1,2^k],[2^k+1,2*2^k]每个长度为2^k的区间进行区间反转

3 k 对([1,2^k],[2^k+1,2*2^k])两两长度为2^k且相邻的区间进行交换

4 l r 询问区间[l,r]的和

思路来源

zbw代码、willingox代码

https://blog.csdn.net/Code92007/article/details/82710068 感觉有翻转单点的影子

题解1

考虑按层打reverse和swap标记,

查询的时候寻找查询区间的真正位置,

比如,pos在一个区间反转的[l,r]区间内的真实位置是其镜像,r-(pos-l)

比如,ql<=mid,但[l,r]发生过左右子树交换,则ql的真实位置为ql+len,

即原先在[l,mid]中,现在在[mid+1,r]中对应位置,len为mid-l+1

按是否完整包含在左/右子树中,拆成三种情况,

对应左右子树是否发生过交换,讨论六种情况

题解2

每一个节点维护一个18位的标记,

第k位表示深度为第k层的标记,在这个点中,有没有下放给儿子,

这里考虑到2、3操作的k一致,k从底往上单增

 

注意到,reverse标记相当于一个要释放到底层的左右子树交换标记,

因为1 2 3 4仅交换左右子树是3 4 1 2的效果,

需要再对3 4和1 2 分别做子树交换,才能做到4 3 2 1的区间翻转效果

而显然,swap标记下放一层即可,1 2 3 4交换之后,就是3 4 1 2

 

每个点,考虑这层需不需要发生子树交换时,只需检查自己那层对应的那一位即可,

标记既代表了这个操作还没有对左右子树进行交换,也代表了还没有下放给左右儿子

代码1

#include <bits/stdc++.h>
using namespace std;
const int N=(1<<18)+5;
typedef long long ll;
int n,q,op,x,y,rev[20],swp[20];
struct segtree{
	int n;
	struct node{int ls,rs,dep;ll v;}e[N<<2];
	#define ls(p) e[p].ls
	#define rs(p) e[p].rs
	#define d(p) e[p].dep
	#define v(p) e[p].v
	void up(int p){v(p)=v(ls(p))+v(rs(p));}
	void bld(int p,int l,int r){
		if(l==r){scanf("%lld",&(v(p)));d(p)=0;return;}
		int mid=l+r>>1;
		bld(ls(p)=(p<<1),l,mid);
		bld(rs(p)=(p<<1|1),mid+1,r);
		d(p)=d(ls(p))+1;
		up(p);
	}
	void init(int _n){n=_n;bld(1,1,n);}
	void chg(int p,int l,int r,int x,ll v){
	    if(rev[d(p)]){
            x=r-(x-l);
	    }
		if(l==r){v(p)=v;return;}
		int mid=(l+r)/2,len=mid-l+1;
        if(swp[d(p)]){
            if(x<=mid)x+=len;
            else x-=len;
        }
		if(x<=mid)chg(ls(p),l,mid,x,v);
		else chg(rs(p),mid+1,r,x,v);
		up(p);
	}
	ll cnt(int p,int l,int r,int ql,int qr){
	    if(rev[d(p)]){
             ql=r-(ql-l);
             qr=r-(qr-l);
             swap(ql,qr);
	    }
		if(ql<=l && r<=qr)return v(p);
		int mid=(l+r)/2,len=mid-l+1;
		ll res=0;
		if(swp[d(p)]){
            if(qr<=mid){
                return cnt(rs(p),mid+1,r,ql+len,qr+len);
            }
            else if(ql>mid){
                return cnt(ls(p),l,mid,ql-len,qr-len);
            }
            else{
                return cnt(ls(p),l,mid,l,qr-len)+cnt(rs(p),mid+1,r,ql+len,r);
            }
		}
		else{
            if(qr<=mid){
                return cnt(ls(p),l,mid,ql,qr);
            }
            else if(ql>mid){
                return cnt(rs(p),mid+1,r,ql,qr);
            }
            else{
                return cnt(ls(p),l,mid,ql,mid)+cnt(rs(p),mid+1,r,mid+1,qr);
            }
		}
	}
}seg;
int main(){
    scanf("%d%d",&n,&q);
    n=1<<n;
    seg.init(n);
    while(q--){
        scanf("%d%d",&op,&x);
        if(op==1){
            scanf("%d",&y);
            seg.chg(1,1,n,x,y);
        }
        else if(op==2){
            rev[x]^=1;
        }
        else if(op==3){
            swp[x+1]^=1;
        }
        else{
            scanf("%d",&y);
            printf("%lld\n",seg.cnt(1,1,n,x,y));
        }
    }
    return 0;
}

代码2

#include <bits/stdc++.h>
using namespace std;
const int N=(1<<18)+5;
typedef long long ll;
int n,q,op,x,y;
struct segtree{
	int n;
	struct node{int ls,rs,rev,swp,dep;ll v;}e[N<<2];
	#define ls(p) e[p].ls
	#define rs(p) e[p].rs
	#define d(p) e[p].dep
	#define v(p) e[p].v
	#define re(p) e[p].rev
    #define sw(p) e[p].swp
	void up(int p){v(p)=v(ls(p))+v(rs(p));}
	void bld(int p,int l,int r){
		re(p)=sw(p)=0;
		if(l==r){scanf("%lld",&(v(p)));d(p)=0;return;}
		int mid=l+r>>1;
		bld(ls(p)=(p<<1),l,mid);
		bld(rs(p)=(p<<1|1),mid+1,r);
		d(p)=d(ls(p))+1;
		up(p);
	}
	void psd(int p){
	    if(re(p)){
            if(re(p)&(1<<d(p))){
                swap(ls(p),rs(p));
                re(ls(p))^=(1<<(d(p)-1));
                re(rs(p))^=(1<<(d(p)-1));
            }
            re(ls(p))^=re(p);
            re(rs(p))^=re(p);
            re(p)=0;
	    }
	    if(sw(p)){
            if(sw(p)&(1<<d(p))){
                swap(ls(p),rs(p));
            }
            sw(ls(p))^=sw(p);
            sw(rs(p))^=sw(p);
            sw(p)=0;
	    }
	}
	void init(int _n){n=_n;bld(1,1,n);}
	void chg(int p,int l,int r,int x,ll v){
		if(l==r){v(p)=v;return;}
		psd(p);
		int mid=(l+r)/2;
		if(x<=mid)chg(ls(p),l,mid,x,v);
		else chg(rs(p),mid+1,r,x,v);
		up(p);
	}
	ll cnt(int p,int l,int r,int ql,int qr){
		if(ql<=l && r<=qr)return v(p);
		psd(p);
		int mid=(l+r)/2;
		ll res=0;
		if(ql<=mid)res+=cnt(ls(p),l,mid,ql,qr);
		if(qr>mid)res+=cnt(rs(p),mid+1,r,ql,qr);
		return res;
	}
}seg;
int main(){
    scanf("%d%d",&n,&q);
    n=1<<n;
    seg.init(n);
    while(q--){
        scanf("%d%d",&op,&x);
        if(op==1){
            scanf("%d",&y);
            seg.chg(1,1,n,x,y);
        }
        else if(op==2){
            seg.e[1].rev^=(1<<x);
        }
        else if(op==3){
            seg.e[1].swp^=(1<<(x+1));
        }
        else{
            scanf("%d",&y);
            printf("%lld\n",seg.cnt(1,1,n,x,y));
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值