最小生成树 找最小值 区间&运算修改

Read problems statements in Mandarin Chinese, Russian and Vietnamese as well.

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

  • 1N, Q105
  • 1Ai, X109
  • 1LRN

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

****为什么要引用线段树的理由需要想清楚,因为数据量太大,区间更新会超时,再区间找出最小值会超时!!!!这才是引用线段树的重要理由,因此下面利用 | (或运算)来寻找区间是否要修改的原因就是,如果对于每一个值都要去修改,那么一定会超时,所以才需要利用.st来保存区间是否与x的&运算有效,如果无效即直接返回,不需要递归到区间每一个值,那么时间复杂度将会大大减少,如果有效,那么找出需要修改的区间,再进一步递归找出需要修改的值。

综上所诉,需要利用.st的原因就是来减少时间复杂度的——这个问题我想了很久,如果把

if(a<=l&&r<=b)
    {
        int t=m[o].st & x;
        if(t==m[o].st) return ;
    }

注释掉,那么直接TLE.

找最小值利用线段树不难,难的在于对&运算理解这题目是利用归并然后与x值对比,如果可以修改,则继续找到最后的可以修改的区间,这样说可能会有点抽象

首先我们可以发现&运算是不可逆的
就是说 假设要&的这个x 某一位是0
他对一个区间的每一个数的这一位进行了&0的操作 那么这一位就永远是0了
这样我们可以发现 一个数真正有意义被&的次数其实最多是含有的1的个数次
如果这个x对这个区间有效 也就是说可以使得1减少那么就暴力递归搞 (线段树)
如何记录x对这个区间有效呢 我们只要记录这个区间 还有哪些位是1就好了



引用别人的一段话
#include <iostream>
#include <algorithm>
#include <cstdio>
#define L o<<1
#define R o<<1|1
using namespace std;
struct node
{
    long long x;
    int l,r;
    long long st;//此题为有条件更新
}m[4000000];
void pushup(int o)
{
    m[o].x=min(m[L].x,m[R].x);
    m[o].st=m[L].st | m[R].st;//与运算,将这个区间里所有数的二进制位数是1的直接取到一起,到时候判断x对l-r区间否有效
    //如果无效则返回,如果有效则更新
}
void build(int o,int l,int r)
{
    m[o].l=l;
    m[o].r=r;
    if(l==r)
    {
        scanf("%lld",&m[o].x);
        m[o].st=m[o].x;
        return ;
    }
    int mid=(l+r)/2;
    build(L,l,mid);
    build(R,mid+1,r);
    pushup(o);
}
long long  query(int o,int l,int r,int a,int b)
{
    if(a<=l&&r<=b)
    {
        return m[o].x;
    }
    int mid=(l+r)/2;
    long long  t=100000000001;
    if(a<=mid)t=min(t,query(L,l,mid,a,b));
    if(b>mid) t=min(t,query(R,mid+1,r,a,b));
    return t;
}
void update(int o,int l,int r,int a,int b,int x)
{
    if(a<=l&&r<=b)//像判断最大值一样,看l-r区间是否在a-b区间里
    {
        int t=m[o].st & x;
        if(t==m[o].st) return ;
    }
    if(l==r)
    {
        m[o].x=m[o].x & x;
        m[o].st=m[o].x;
        return ;
    }
    int mid=(l+r)/2;
    if(a<=mid) update(L,l,mid,a,b,x);
    if(b>mid) update(R,mid+1,r,a,b,x);
    pushup(o);
}
int main()
{

    int n,m,i,j,k;
    while(~scanf("%d%d",&n,&m))
   {

    build(1,1,n);
    while(m--)
    {
        scanf("%d",&i);
        if(!i)
        {
            scanf("%d%d",&i,&j);
            printf("%d\n",query(1,1,n,i,j));
        }
        else
        {
            scanf("%d%d%d",&i,&j,&k);
            update(1,1,n,i,j,k);
        }
    }
   }
}

先上代码,最难理解的是m[o].st=m[L].st | m[R].st相当于对L,R进行归并将他们的所有含有1的位数全部归并到m[o].st上,因此如果x & m[o].st没有变化,也就是说,对区间所有元素都不需要修改,则返回,这一点理解了代码看起来也就比较轻松了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值