【SDOJ2820】or(或)

题面

构造一个长度为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。

对于30%的数据,n,m<=1000。 

对于另外30%的数据,pi<=1。

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

分析

几个显然的结论

1.有0的位置必须所有数那一位全部为0

2.有1的位置必须有一个数那一位为1

所以其实可以拿p来构造,把所有数都设为230-1(二进制所有位都是1)

拿p来&一下,原来是1的位置遇到0就会被弄成0,遇到1不会变,按照题目的意思去尝试修改(区间修改,线段树)

最后需要检验一下,就区间取或

这有一个结论

(a&p)|(b&p)……(z&p)=(a|b|……|z)&p

这就是为啥线段树可以合法合并

代码

#include<bits/stdc++.h>
using namespace std;
#define N 100100
#define ll long long 
#define MAXN (1<<30)-1
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (t[p].l+t[p].r>>1)
ll n,m;
ll p[N],pl[N],pr[N];
struct email
{
    ll l,r,sum;
}t[N*4];

inline void pushup(ll p)
{
    t[p].sum=t[lc].sum|t[rc].sum;
} 

inline void pushdown(ll p)
{
    ll px=t[p].sum;
    t[lc].sum&=px;t[rc].sum&=px;
}

inline void build(ll p,ll l,ll r)
{
    t[p].l=l;t[p].r=r;
    if(l==r)
    {
        t[p].sum=MAXN;
        return ;
    }
    ll bm=l+r>>1;
    build(lc,l,bm);build(rc,bm+1,r);
    pushup(p);
}

inline void update(ll p,ll ql,ll qr,ll x)
{
    if(ql<=t[p].l&&qr>=t[p].r)
    {
        t[p].sum&=x;
        return ;
    }
    pushdown(p);
    if(ql<=mid)update(lc,ql,qr,x);
    if(qr>mid)update(rc,ql,qr,x);
    pushup(p);
}

inline ll query(ll p,ll ql,ll qr)
{
    ll ret=0;
    if(ql<=t[p].l&&qr>=t[p].r)
        return t[p].sum;
    pushdown(p);
    if(ql<=mid)ret|=query(lc,ql,qr);
    if(qr>mid)ret|=query(rc,ql,qr);
    return ret;
}

int main()
{
    scanf("%lld%lld",&n,&m);
    build(1,1,n);
    for(ll i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld",&pl[i],&pr[i],&p[i]);
        update(1,pl[i],pr[i],p[i]);
    }
    for(ll i=1;i<=m;i++)
    {
        ll ret=query(1,pl[i],pr[i]);
        if(ret!=p[i])
        {printf("No\n");return 0;}
    }
    printf("Yes\n");
    for(ll i=1;i<=n;i++)
        printf("%lld ",query(1,i,i));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/NSD-email0820/p/9692002.html