首页 > 其他分享 >CF1401F Reverse and Swap 题解

CF1401F Reverse and Swap 题解

时间:2024-02-07 10:15:13浏览次数:29  
标签:CF1401F Reverse int 题解 线段 long times now 翻转

解题思路

巧妙的数据结构题,非常巧妙的利用的线段树的奇妙性质。

操作逐条维护:

  • Replace: 直接线段树上单点修改;
  • Sum:线段树查询区间和;
  • Reverse:考虑线段树的形态。线段树第 \(i\) 层(除最后一层)有 \(2^{i-1}\) 个节点,那么将所有 \(i\ge 1\) 的区间 \([(i-1)\times 2^k,i\times 2^k]\) 翻转,可以看作将线段树每一层均翻转一次。考虑用一个标记来表示每一层是否被翻转,因为翻转两次后相当于没有翻转,那么直接使用异或来维护即可,即每一次翻转操作让每一层的标记异或 \(1\);
  • Swap:观察两个被交换的区间,肉眼可见在除以 \(2\) 之后就变成了一个 \([(i-1)\times 2^k,i\times 2^k]\) 的区间,而两个区间刚好各占一半。由于题目中唯一的查询操作是查询区间和,那么交换操作和翻转操作是等价的,那么直接修改第 \(k+1\) 层的标记即可。

需要注意的是,在这种维护层的顺序下,区间 \([1,2^n]\) 实际上是位于第 \(n\) 层的,然后不要忘了开 long long。

AC 代码

#include<stdio.h>
#include<stdlib.h>
#define int long long
#define N (1<<18)+5
int n,q,a[N],Mark[N];
int tree[N<<1];
#define l(x) (x<<1)
#define r(x) ((x<<1)|1)
#define mid ((l+r)>>1)
inline void Push_Up(int now){
    tree[now]=
        tree[l(now)]+
        tree[r(now)];
}
inline void Update(int now,int l,int r,int p,int v,int depth){
    if(p>r||p<l) return;
    if(l==r){
        tree[now]=v;
        return;
    }
    if(Mark[depth]){
        Update(l(now),mid+1,r,p,v,depth-1);
        Update(r(now),l,mid,p,v,depth-1);
    }else{
        Update(l(now),l,mid,p,v,depth-1);
        Update(r(now),mid+1,r,p,v,depth-1);
    }Push_Up(now);
}
inline int Query(int now,int l,int r,int L,int R,int depth){
    if(L>r||R<l) return 0;
    if(L<=l&&r<=R)
        return tree[now];
    int Lans,Rans;
    if(Mark[depth]){
        Lans=Query(l(now),mid+1,r,L,R,depth-1);
        Rans=Query(r(now),l,mid,L,R,depth-1);
    }else{
        Lans=Query(l(now),l,mid,L,R,depth-1);
        Rans=Query(r(now),mid+1,r,L,R,depth-1);
    }return Lans+Rans;
}
signed main(){
    scanf("%lld%lld",&n,&q);
    for(register int i=1;i<=1<<n;++i)
        scanf("%lld",&a[i]);
    for(register int i=1;i<=1<<n;++i)
        Update(1,1,1<<n,i,a[i],n);
    int opt,x,k;while(q--){
        scanf("%lld",&opt);
        switch (opt){
            case 1:{
                scanf("%lld",&x);
                scanf("%lld",&k);
                Update(1,1,1<<n,x,k,n);
                break;
            }
            case 2:{
                scanf("%lld",&k);
                for(register int i=0;i<=k;++i)
                    Mark[i]^=1;break;
            }
            case 3:{
                scanf("%lld",&k);
                Mark[k+1]^=1;
                break;
            }
            default:{
                scanf("%lld",&x); scanf("%lld",&k);
                printf("%lld\n",Query(1,1,1<<n,x,k,n));
                break;
            }
        }
    }
}

标签:CF1401F,Reverse,int,题解,线段,long,times,now,翻转
From: https://www.cnblogs.com/UncleSamDied6/p/18010660

相关文章

  • CF1408E Avoid Rainbow Cycles 题解
    解题思路第一眼看过去感觉不是很可做……但是我们可以发现,如果有两个点在不同的集合中出现过,那么一定会存在彩虹环,那么两个点最多出现一次。同时我们考虑将题意转化一下,变成求最大能选取的点,使得不出现彩虹环。根据刚刚的性质,我们可以考虑每个点向它所在的集合连一条边权为\(a_......
  • CF1338C Perfect Triples 题解
    解题思路没什么好说的,就是打表找规律……表在这里不难发现,三元组中第一个数的最后两位按照\(00\to01\to10\to11\)的顺序变化,其他位也一样,同样,第二个数和第三个数中每两位分别按照\(00\to10\to11\to01\)和\(00\to11\to01\to10\)的顺序变化,且与第一个数对应变化......
  • CF1379C Choosing flowers 题解
    解题思路不是那么显然的,当只选一种\(b_i\)或全选\(a_i\)时最优。那么我们可以先对\(a_i\)从大到小排序,枚举每一个\(b_i\),然后二分找到第一个大于等于\(b_i\)的\(a_j\),判断\(a_1\sima_j\)中是否包含\(a_i\),如果包含,当前的答案为\(\displaystyle\left(\sum_{k=1}^{......
  • CF1385F Removing Leaves 题解
    解题思路简单贪心,优先选择叶子节点最多的,这样能够保证一定能把所有能删的都删了。因为要建一个可删除的图,所以我们可以使用set来存边,不然就需要维护一堆东西……那么我们肯定是从有叶子节点的点向父亲更新的,那么我们每次选择叶子节点最多的点,然后删除\(k\)个叶子,判断一下删......
  • CF1415E New Game Plus! 题解
    解题思路简单贪心题,我们可以把整个序列看作\(k+1\)个可重集,首先可以得到一个显然的结论:较大的数一定比较小的数先放入一个集合中。同样,由于每一轮\(ans\getsans+\maxsum_i\),其中\(sum_i\)表示第\(i\)个集合的元素和,那么,我们一定会将当前的元素放入当前和最大的哪个集合......
  • CF1416B Make Them Equal 题解
    解题思路观察可以发现,每次操作后序列元素之和不变,那么我们可以将每一次操作看作是\(a_i\)向\(a_j\)移动了\(i\timesx\)。由此可得,若序列和\(sum\bmodn\not=0\),那么一定无解,否则一定存在一个合法的操作方案。因为每次移动时移动的数应为\(i\)的倍数,所以\(a_1\)可以向......
  • CF1446C Xor Tree 题解
    解题思路与其考虑删除哪些点,不如考虑保留哪些点。考虑到和异或有关,那么我们可以把这些数倒序插入trie树中,然后我们就可以在trie树上跑一个简单的dp:若当前节点为叶子节点,那么保留,返回\(1\);若当前节点在链上,那么直接继承儿子节点;若当前节点有两个儿子,那么更新为较大儿子......
  • CF1922E Increasing Subsequences 题解
    解题思路因为可以有空集,那么我们首先构造第一段最长的连续上升的序列,那么这段序列中共有\(2^{\mids\mid}\)个上升子序列。接下来我们考虑补全剩余的,我们不妨将剩余的部分全部设为连续不增序列,那么设当前位置在第一段中有\(k\)个小于它的,那么添加这个数后可以增加\(2^{k-1}......
  • CF1413C Perform Easily 题解
    解题思路其实是很简单的一道题,考虑计算出所有\(b_i\)在减去每一个\(a_j\)后所有可能的值,将这个值按照从小到大的顺序排序,那么我们可以考虑固定最小值,查找最大值,这个时候从最小值到最大值的区间内应该每一种\(b_i\)都出现了一次。那么,我们可以使用一个桶或者map搭配双指针......
  • 洛谷P10135 暨 USACOJan2024S T2 题解
    题意简述给点一棵有\(n\)个节点的树,在每个时间点都会在某个节点出现一瓶药水,记\(p_i\)为第\(i\)个时间点出现药水的节点,定义一次遍历为从\(1\)号节点走到任意节点,要求在遍历次数最少的情况下拾取最多数量的药水。思维路径首先我们要探讨遍历次数最少的状态是怎样的。由......