线段树/学会分析每个数值最多被修改的次数

You are given an array of N integers. You should support the following queries on this array.

  • 0 L R : Find the minimum integer in the range AL, AL+1, ..., AR.
  • 1 L R X : You should apply the assignment A[i] = A[i] & X, for all indices i in range [L, R], where & denotes bitwise AND operation.

Input

First line of the input contains two space separated integers N and Q.

Second line contains N integer numbers denoting array A.

In the next Q lines, each contain one of the queries described above.

Output

For each query of the type 0, output a single line containing the answer of the query.

Constraints

  • 1 ≤ N, Q ≤ 105
  • 1 ≤ AiX ≤ 109
  • 1 ≤ L ≤ R ≤ N

Example

Input:
5 5
1 5 2 3 4
0 2 5
1 1 5 6
0 2 2
1 2 5 3
0 1 3

Output:
2
4
0

题目描述 给定长度为 N 的整数序列。你需要支持如下操作: • 0 L R:查询序列在区间 [L, R] 中的最小值; • 1 L R X:对于序列在区间 [L, R] 中的每个元素 Ai,执行操作:Ai ← Ai&X,其中 & 为按 位与操作。

分析 区间查询最小值可以用线段树,这道题麻烦的是更新操作,若是对区间里每个数都按位与一遍,必定会超时。这时不要慌,稳定分析,&按位与有个特点,即该数的二进制表示为某位为0的话,此后的所以更新,都不会改变该位为0的事实。这样我们可以计算这个区间究竟最多能进行多少次&操作?即区间内每个数的每个对应位上1的总数,若某次进行&x操作时,检测一下会不会对区间造成影响,若不会,就此打住,这样就避免了许多无用操作,不必每次都更新到叶子结点上。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include <queue>
#include <vector>
#include<bitset>
#include<map>
#include<deque>
using namespace std;
typedef long long LL;
const int maxn = 1e5+5;
const int mod = 77200211+233;
typedef pair<int,int> pii;
#define X first
#define Y second
#define pb push_back
//#define mp make_pair
#define ms(a,b) memset(a,b,sizeof(a))
const int inf = 0x3f3f3f3f;
#define lson l,m,2*rt
#define rson m+1,r,2*rt+1

struct node{
    int a;
    int l,r;
    int st;
    int mid(){
        return (l+r)>>1;
    }
}tree[maxn<<2];

void pushup(int rt){
    tree[rt].a = min(tree[rt<<1].a,tree[rt<<1|1].a);
    tree[rt].st = tree[rt<<1].st | tree[rt<<1|1].st;
}

void build(int l,int r,int rt){
    tree[rt].l=l,tree[rt].r=r;
    if(l==r){
        scanf("%d",&tree[rt].a);
        tree[rt].st=tree[rt].a;
        return;
    }
    int m=tree[rt].mid();
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}

void update(int l,int r,int x,int rt){
    if(l<= tree[rt].l && r>= tree[rt].r){
        int t = tree[rt].st&x;
        if(t==tree[rt].st) return;
    }
    if(tree[rt].l==tree[rt].r){
        tree[rt].a = tree[rt].a & x;
        tree[rt].st=tree[rt].a;
        return;
    }
    int m = tree[rt].mid();
    if(l<=m) update(l,r,x,rt<<1);
    if(r>m) update(l,r,x,rt<<1|1);

    pushup(rt);
}

int query(int l,int r,int rt){
    if(l<= tree[rt].l && r>= tree[rt].r){
        return tree[rt].a;
    }
    int m=tree[rt].mid();
    int ans=inf;
    if(l<=m) ans=min(ans,query(l,r,rt<<1));
    if(r>m) ans=min(ans,query(l,r,rt<<1|1));
    return ans;
}

int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        build(1,n,1);

        while(m--){
            int x,l,r;
            scanf("%d",&x);
            if(x){
                int l,r;
                scanf("%d%d%d",&l,&r,&x);
                update(l,r,x,1);
            }else{
                scanf("%d%d",&l,&r);
                cout<<query(l,r,1)<<endl;
            }
        }
    }
    return 0;
}

 

原文地址:https://www.cnblogs.com/fht-litost/p/8654722.html

时间: 2024-11-05 16:27:19

线段树/学会分析每个数值最多被修改的次数的相关文章

基本线段树模板(建树、点/区间修改、查询)

线段树主要用于区间记录信息(如区间和.最大最小值等),首先是建树: 这里以求和为例: 1 const int MAXM=50000; //定义 MAXM 为线段最大长度 2 3 int a[MAXM+5],segtree[(MAXM<<2)+5]; // a 数组为 main 函数中读入的内容,segtree 数组为需要查询的数的信息(如和.最值等),树的空间大小为线段最大长度的四倍 4 5 void build(int o,int l,int r){ //传入的参数为 o:当前需要建立的结点

[BZOJ 3995] [SDOI2015] 道路修建 【线段树维护连通性】

题目链接:BZOJ - 3995 题目分析 这道题..是我悲伤的回忆.. 线段树维护连通性,与 BZOJ-1018 类似,然而我省选之前并没有做过  1018,即使它在 ProblemSet 的第一页. 更悲伤的是,这道题有 40 分的暴力分,写个 Kruskal 就可以得到,然而我写了个更快的 DP . 这本来没有什么问题,然而我的 DP 转移少些了一种情况,于是...爆零.没错,省选前20名可能就我没有得到这 40 分? 不想再多说什么了...希望以后不要再这样 SB 了,如果以后还有机会的

线段树例题及做题误区

学会了一系列的线段树之后发现 除了扫描线还不是很熟之外一些操作基本上是得心应手了. 但是仍是很菜,在此再次深有感悟 以后做题再看题解 直接剁手 我就不信不看题解自己的思路出现错误 每次都当我 有了正确的思路之时 却被一些 很迷的思路 误导去看题解,看完题解之后才恍然大悟 .这点需要注意!!!我想我都窥出正解了为什么不能再多想想呢? 真的是超级没有成就感 感觉是非常难受的 好题被自己一时看了题解毁了这是我作为一个正在学习的人所极不想看见的. . 这道题还不错 对线段树是一个考察 如果能仔细思考的话

线段树——HYSBZ - 3224

题目含义 题目都给出来了,要你写个线段树 题目分析 只要学会了模板,这种题就很简单了 题目代码 注:不管怎样,首先要试着默写出来通过一次 #include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; typedef long long LL; const int maxn=1e6+7; int a[maxn],b[maxn],l

HUT 线段树练习 部分题解

F-poj 2886 这题去年月神给我们14级的抓过.然而比较偏数学. 题意大概是n个小朋友围成一圈,每个小朋友手里都有一张卡片,卡片上有个数字a[i]. 从第k个小朋友开始,第k个小朋友出圈,然后让他的左手方向的第a[k]个小朋友出圈.然后这个小朋友又根据规则让另一个小朋友出圈. 第p个出圈的小朋友拿到的糖果数目为p的因子个数,问谁拿到了最多的糖果 可以打反素数表的方式来做(百度即可),但这里介绍另一种方法,就是我们可以直接dfs出小于n的,因子个数最多的数 因为要使因子个数尽量的多,该数应该

浅谈线段树

 数据结构——线段树 O.引例 A.给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出. 一种回答:这也太简单了,O(n)枚举搜索就行了. 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定. 那好,我再修改一下题目. B.给出n个数,n<=100,和m个操作,每个操作可能有两种:1.在某个位置加上一个数:2.询问区间[l,r]的和,并输出. 回答:o(n)枚举. 动态修改最起码不能用静态的前缀和做了. 好,我再修改题目: C.给出n个数,n<=1000000,

线段树讲解(转)

转自   http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html  数据结构——线段树 O.引例 A.给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出. 一种回答:这也太简单了,O(n)枚举搜索就行了. 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定. 那好,我再修改一下题目. B.给出n个数,n<=100,和m个操作,每个操作可能有两种:1.在某个位置加上一个数:2.询问区间[l,r]的和,并输出. 回答:

P1243~P1247 线段树模板题总结

前言 这几天刚刚刷了5道线段树(水)题,现在来总结一下. 首先是犯的不少错误: 1.建树.更新函数没有return.这是最气的,每次最后程序错误查了半天也没查出来,最后发现是没有return.递归边界要return,递归边界要return,递归边界要return,重要的事情说三遍. 2.判断查找区间于线段的变量写反.听说这个是常犯错误. 然后说一下学线段树的收获.线段树的代码量的确多,很能练自己的思维,而且学的过程中简单的理解了一下#define的用处.线段树用来解决区间查询,区间修改都很方便,

【BZOJ 4662】 4662: Snow (线段树+并查集)

4662: Snow Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 136  Solved: 47 Description 2333年的某一天,临冬突降大雪,主干道已经被雪覆盖不能使用.城 主 囧·雪 决定要对主干道进行一次清扫. 临冬城的主干道可以看为一条数轴.囧·雪 一共找来了n个清理工,第 i个清理工的工作范围为[li,ri],也就是说这个清理工会把[li,ri]这一 段主干道清理干净(当然已经被清理过的部分就被忽略了).当然有可能主 干道