codeforces 482 B Interesting Array 线段树区间更新

题目链接:http://codeforces.com/problemset/problem/482/B
题意:
给定n,表示序列的长度,给m个操作,每个操作有l,r,p, 表示al~ar 按位与起来结果是p。对于这m个操作能不能找出一个满足的序列长度为n。如果可以输出yes,并输出序列。否则no。
思路:
运用线段树维护每个区间的&结果。
假设已经有第一个操作对一个区间进行了,那么现在这个区间的&结果就是这个p1,现在又有一个操作对另一个区间进行操作,但是这两个区间是有重叠部分的。考虑这个重叠的部分,要满足跟前一个区间的元素的&结果是p1,与后面一个区间的元素的&结果是p2,要达到这样的效果,就要用之前的结果p1按位或上p2。这样这个重叠部分就可以满足两个&了。
那么可以这样做将线段树初始化为0,对于每一个操作,将这个区间的元素都按位或上p,如何检查是否能够找到这样的序列呢?所有操作结束之后,对于每一个操作再进行一次查询,如果与操作不符,就是no,如果都是相符的就是yes,最后输出整个区间的所有元素就可以了。
线段树区间更新:
区间更新是指更新某个区间内的叶子节点的值,因为涉及到的叶子节点不止一个,而叶子节点会影响其相应的非叶父节点,那么回溯需要更新的非叶子节点也会有很多,如果一次性更新完,操作的时间复杂度肯定不是O(lgn)。
要注意的就是一个懒惰操作,对于每个节点增加一个延迟标记,记录是否进行了更新。
更新p节点的同时将这个节点的延迟标记更新。
在进行更新和查询的时候,如果我们到了一个节点p,并且决定考虑其子节点,那么我们就要看节点p是否被标记,如果有,就要按照标记修改其子节点的信息,并且给子节点都标上相同的标记,同时消掉节点p的标记。
在本题上的运用:
本题中的laz 就是延迟标记,记录的是这个要按位或的值,如果有这个标记,就按照标记更新子节点的信息,并将子节点同时标记上。

感觉线段树运用好广泛,维护的东西也各种各样,还是得在题目中多感悟吧~

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 100009
#define INF (1e18)-1
int n,m;
int l[M],r[M],p[M];
int laz[M<<2],val[M<<2];
void pushup(int rt)
{
    val[rt] = val[rt<<1] & val[rt<<1|1];
}
void pushdown(int rt)
{
    if(laz[rt])
    {
        val[rt<<1] |= laz[rt];
        val[rt<<1|1] |= laz[rt];
        laz[rt<<1] |= laz[rt];
        laz[rt<<1|1] |= laz[rt];
        laz[rt] = 0;
    }
}
void build(int l,int r,int rt)
{
    laz[rt] = 0;
    if(l == r)
    {
        val[rt] = 0;
        return ;
    }
    int m = (l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}
void update(int a,int b,int c,int l,int r,int rt)
{
    if(a <= l && r <= b)
    {
        val[rt] |= c;
        laz[rt] |= c;
        return ;
    }
    pushdown(rt);
    int m = (l+r)>>1;
    if(a <= m) update(a,b,c,l,m,rt<<1);
    if(b > m) update(a,b,c,m+1,r,rt<<1|1);
    pushup(rt);
}
int query(int a,int b,int l,int r,int rt)
{
    if(a <= l && r <= b)
    {
        return val[rt];
    }
    pushdown(rt);
    int m = (l+r)>>1;
    int ret = INF;
    if(a <= m) ret &= query(a,b,l,m,rt<<1);
    if(b > m) ret &= query(a,b,m+1,r,rt<<1|1);
    return ret;
}
bool judge()
{
    for(int i = 0;i < m;i++)
    {
        if(query(l[i],r[i],1,n,1) != p[i])
            return true;
    }
    printf("YES\n");
    for(int i = 1;i < n;i++)
    {
        printf("%d ",query(i,i,1,n,1));
    }
    printf("%d\n",query(n,n,1,n,1));
    return false;
}
int main()
{
    while(scanf("%d %d",&n,&m)==2)
    {
        build(1,n,1);
        for(int i = 0;i < m;i++)
        {
            scanf("%d %d %d",&l[i],&r[i],&p[i]);
            update(l[i],r[i],p[i],1,n,1);
        }
        if(judge())
            printf("NO\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值