2018.08.22 【校内模拟】 T2 位运算 Or (线段树)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/81945634

【描述】

构造一个长度为 n 的非负整数序列 x,满足 m 个条件,第 i 个条件为
x[li] | x[li+1] | … | x[ri]=pi。

【输入】

第一行两个整数 n,m。接下来 m 行每行三个整数 li,ri,pi。

【输出】

如果存在这样的序列 x,第一行输出 Yes,第二行输出 n 个不超过 2^30-1 的非负整数表示x[1]~x[n],否则输出一行 No。 ( z x y o i : S P J )

【输入样例 】

2 1
1 2 1

【输出样例 】

Yes

【子任务】

对于 30%的数据, n,m<=1000。
对于另外 30%的数据, pi<=1。
对于 100%的数据, n,m<=100000, 1<=li<=ri<=n, 0<=pi<2^30。


BB:

好的又是一道位运算。。。
怎么gonze这么喜欢考位运算。。。
因为不会卡我们常数???

好的进入主题。


解析:

这次的位运算只有一种,就是or运算。

给出限制的方式也很玄学,限制区间or的结果。。。

区间。。。emmmm…

于是开始考虑区间处理的奇技淫巧。。。

于是想到了线段树。

先看一下每一个限制对结果的实际影响:

  1. 各位or结果互不影响
  2. 若or结果中该位为1,则该区间该位必有至少一个1
  3. 若or结果中该位为0,则该区间所有位都为0。。

第一条,说不定可以各位分开处理(我后座的dalao,似乎建立了30颗线段树,卡常数卡出 60 p t s ),但是实际上不用。。
第二条,没什么屁用。

第三条,我们可以将所有数初始化为$2^{30}-1,对于每一个限制,区间and一下就好了,这样就能保证所有数在0的位上满足了限制。
但是这样可能无法满足区间中至少有一个1的条件,所以最后再次检验是否满足原限制。

总结起来,就是线段树区间and,询问区间or的结果。
还有一个小优化。就是可以在区间修改的时候记录是否合法,不合法直接输出“No”,优化效果不太强,而且调试难度较大(虽然 z x y o i 已经调出来了),这里就贴一个一般的算法吧。


代码:

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define cs const
#define gc getchar
#define pc putchar
#define st static
#define uint unsigned int

inline
uint getint(){
    st uint num;
    st char c;
    for(c=gc();!isdigit(c);c=gc());
    for(num=0;isdigit(c);c=gc())num=(num<<1)+(num<<3)+(c^48);
    return num;
}

inline
void outint(uint a) {
    st char ch[23];
    if(a==0)return (void)pc('0');
    while(a)ch[++ch[0]]=(a-a/10*10)^48,a/=10;
    while(ch[0])pc(ch[ch[0]--]);
}

int n,m;
const uint bas=(1<<30)-1;
uint num[400005];
uint a[100005];

inline
void build(int k,int l,int r){
    num[k]=bas;
    if(l==r)return ;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}

inline
void pushdown(int k){
    num[k<<1]&=num[k];
    num[k<<1|1]&=num[k];
}

inline
void update(int k,int l,int r,cs int &ql,cs int &qr,cs uint &val){
    if(ql<=l&&r<=qr){
        num[k]&=val;
        return ;
    }
    pushdown(k);
    int mid=(l+r)>>1;
    if(ql<=mid)update(k<<1,l,mid,ql,qr,val);
    if(qr>mid)update(k<<1|1,mid+1,r,ql,qr,val);
}

inline
uint queryor(int k,int l,int r,cs int &ql,cs int &qr){
    if(ql<=l&&r<=qr){
        return num[k];
    }
    pushdown(k);
    uint ans=0;
    int mid=(l+r)>>1;
    if(ql<=mid)ans|=queryor(k<<1,l,mid,ql,qr);
    if(qr>mid)ans|=queryor(k<<1|1,mid+1,r,ql,qr);
    return ans; 
}

inline
void pushall(int k,int l,int r){
    if(l==r)return (void)(a[l]=num[k]);
    pushdown(k);
    int mid=(l+r)>>1;
    pushall(k<<1,l,mid);
    pushall(k<<1|1,mid+1,r);
}

int l[100005],r[100005];
uint c[100005];

int main(){
    n=getint();
    m=getint();
    build(1,1,n);
    for(int re i=1;i<=m;++i){
        l[i]=getint();
        r[i]=getint();
        c[i]=getint();
        update(1,1,n,l[i],r[i],c[i]);
    }
    for(int re i=1;i<=m;++i){
        if(queryor(1,1,n,l[i],r[i])!=c[i]){
            puts("No");
            return 0;
        }
    }
    pushall(1,1,n);
    puts("Yes");
    for(int re i=1;i<=n;++i){
        outint(a[i]),pc(' ');
    }
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/81945634