首页 > 其他分享 >[拆位线段树]RMQ

[拆位线段树]RMQ

时间:2022-11-25 19:43:34浏览次数:81  
标签:lazy RMQ int 线段 mid ch sum root 拆位


[拆位线段树]RMQ

题目

​https://ac.nowcoder.com/acm/problem/21414​[拆位线段树]RMQ_区间求和

思路

区间或,区间求和
对于区间或,异或这种位运算,没法之间打懒标记。但是如果我们按位拆分,可以发现对于原数组都为01的线段树来说,或运算等效于区间设1。那么我们对每一位进行区间设1,区间求和操作,然后再最终求解答案的时候带上位权即可

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=2e5+10;
int A[MAXN];
bool A_bit[MAXN][25];
struct tnode
{
bool lazy[25];
LL sum[25];
int l, r;
};
struct Segment_Tree
{
tnode t[4 * MAXN];
void init_lazy(int root,int i){
t[root].lazy[i]=0;
}
void union_lazy(int fa, int ch,int i){
t[ch].lazy[i]|=t[fa].lazy[i];
}
void cal_lazy(int root,int i){
t[root].sum[i]=(t[root].r-t[root].l+1);
return;
}
void push_down(int root,int i)
{
if (t[root].lazy[i])
{
cal_lazy(root,i);
if (t[root].l != t[root].r)
{
int ch = root << 1;
union_lazy(root, ch,i);
union_lazy(root, ch + 1,i);
}
init_lazy(root,i);
}
}
void update (int root,int i)
{
int ch = root << 1;
push_down(ch,i);
push_down(ch + 1,i);
t[root].sum[i]=t[ch].sum[i]+t[ch+1].sum[i];
}
void build(int root, int l, int r,int i)
{
t[root].l = l;t[root].r = r;
init_lazy(root,i);
if (l != r)
{
int mid = (l + r) >> 1;
int ch = root << 1;
build(ch, l, mid,i);
build(ch + 1, mid + 1, r,i);
update(root,i);
}
else{
t[root].sum[i]=A_bit[l][i];
}
}
void change(int root, int l, int r,int i)
{
push_down(root,i);
if (l == t[root].l && r == t[root].r)
{
t[root].lazy[i] = 1;
return;
}
int mid = (t[root].l + t[root].r) >> 1;
int ch = root << 1;
if (r <= mid)change(ch, l, r, i);
else if (l > mid)change(ch + 1, l, r, i);
else {change(ch, l, mid, i); change(ch + 1, mid + 1, r,i);}
update(root,i);
}
LL sum(int root, int l, int r, int i)
{
push_down(root,i);
if (t[root].l == l && t[root].r == r){
return t[root].sum[i];
}
int mid = (t[root].l + t[root].r) >> 1;
int ch = root << 1;
if (r <= mid)return sum(ch, l, r, i);
else if (l > mid)return sum(ch + 1, l, r, i);
else return sum(ch, l, mid, i) + sum(ch + 1, mid + 1, r, i);
}
};
Segment_Tree ST;
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&A[i]);
for(int i=1;i<=n;i++){
int j=0;
while(A[i]){
if(A[i]&1)A_bit[i][j++]=1;
else A_bit[i][j++]=0;
A[i]>>=1;
}
}
for(int i=0;i<=20;i++)ST.build(1, 1, n, i);

while(m--){
string op;cin>>op;
if(op=="SUM"){
int l,r;scanf("%d%d",&l,&r);
LL ans=0;
for(int i=0;i<=20;i++)ans+=(1LL<<i)*ST.sum(1,l,r,i);
printf("%lld\n",ans);
}
else{
int l,r,x;scanf("%d%d%d",&l,&r,&x);
int i=0;
while(x){
if(x&1)ST.change(1,l,r,i);
x>>=1,i++;
}
}
}
return 0;
}


标签:lazy,RMQ,int,线段,mid,ch,sum,root,拆位
From: https://blog.51cto.com/u_15891800/5887529

相关文章

  • [线段树 多tag]D-数据结构
    [线段树多tag]D-数据结构题目思路多tag的线段树有时序性问题,因此不能直接把标记累计。例如:对于同样两个标记+和*,ax+b和(x+b)*a是不一样的,因此我们需要设计tag合并的规则......
  • 303. 区域和检索 - 数组不可变,307. 区域和检索 - 数组可修改(线段树)
    介绍线段树的题目,起步基本就是hard。其实线段树就是一种经典空间换时间,用一维度的空间降了一维度的时间。当然,使用线段树也要满足一些条件,即数据的组织结构要有特点。一......
  • 基础代码-线段
    问题B:线段时间限制:1Sec  内存限制:128MB题目描述1.询问+LR增加一条线段[L,R],你的程序应该输出有多少条线段被该线段包含(非严格)。2.询问-L......
  • 权值线段树
    对权值作为维护对象而开的线段树,即每个点存的是区间内对应数字的某种值(出现的次数)。权值线段树用于维护一个数在一个序列中出现的次数比如数值1,1,2,2,2,3,4,5,6,7,8初始每个节点......
  • 动态开点线段树
    简单来说就是,你要用到一个点才开那个点,不用的点不开,可以大幅节省空间。这样空间复杂度可以大致降到O(nlogn)。是不是很棒。接下来是实现:一开始,你只有一个根节点。通过updat......
  • P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    有一说一,雨天的尾巴我其实骂了很久。主要是题面之前一直没耐心读,然后后面在其他地方看到了形式化题意,就做掉了。其实感觉有很多题都比这玩意适合当板子,所以这个迟到的板子......
  • 线段树小技巧
    如果发现直接线段树维护\(val\)不方便,不妨思考让线段树维护\(val\)的差分数组,说不定这样可以让你豁然开朗哦!当然这也为平时的练习还有比赛提供了一个思考方向。例题:......
  • 线段树入门
    线段树是个好东西,首先要知道这些点:1.线段树适用于任何区间修改和区间查询的操作,复杂度只有O(logn)贼快2.线段树没有树状数组好写呢,其实也不难3.线段树每一个点是管理......
  • CF803G Periodic RMQ Problem
    这题很妙,当时用CD的方法,写平衡树,但是吧没有领会精神,写了200多行,发现前驱后继又不合法的情况,好像要写12种情况,就不想写了。然后就突然明白线段树做法了。。。介绍一种线段......
  • 【SSL 1590】旅游(线段树优化DP)
    旅游题目链接:SSL1590题目大意要从x号点依次按编号走到y号点,每次可以选择跳最多z个点,即从i到i+z。每到一个点都要支付a的费用,到一些给出的特定点有其对应的......