poj2777线段树区间染色

网上的代码有很多但是实现的方式不同,我个人还是比较喜欢带结构体的线段树,

然后本题的关键就在于维护颜色的问题上了,

结构体中的2个变量sum表示是第几种颜色,初始化为1

lazy表示之前是否染过色

如果染过色就把染过的色下传,然后在进行染色

然后就是判断这个点所代表区间的颜色,如果左右区间颜色不同,就令他的颜色为-1,相同就等于左右区间的颜色。

 

#include<iostream>
#include<algorithm>
#include<fstream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<cctype>
#include<vector>
#include<limits.h>
#include<queue>
#include<stack>

using namespace std;

const int maxn=1e5+5;

struct mmp
{
    int l,r;
    int  sum,lazy;      //区间左右边界,区间元素之和,懒惰标记
    mmp()
    {
        l=0;
        r=0;
        sum=0;
        lazy=0;
    }
};

mmp m[4*maxn];		//线段树的内容从下标1开始存
int p[100];
int ans;



//初始化
void build(int k,int l,int r)   //节点编号,左右边界
{
    m[k].l=l;
    m[k].r=r;
    m[k].sum=1;
    m[k].lazy=-1;
    if(l==r)    //递归到叶子结点
    {
        return ;
    }
    int mid=(l+r)/2;
    build(k*2,l,mid);
    build(k*2+1,mid+1,r);
}

void update(int k)      //更新节点k的sum
{
    if(m[k*2].sum==m[k*2+1].sum)
    {
        m[k].sum=m[k*2].sum;
    }
    else
        m[k].sum=-1;
    //一段区间的元素和等于它的子区间的元素和
}

//下传标记
void pushdown(int k)    //将点k的懒惰标记下传
{
    if(m[k].l==m[k].r)  //如果节点k已经是叶子节点了
    {
        return ;
    }
    //给k的子节点重新赋值
    m[k*2].sum=m[k].lazy;
    m[k*2+1].sum=m[k].lazy;
    m[k*2].lazy=m[k].lazy;   //下传点k的标记
    m[k*2+1].lazy=m[k].lazy;
    m[k].lazy=-1;
}

//区间修改
void changeSegment(int k,int l,int r,int x)
//当前节点编号为k,把区间[l,r]内的所有元素+x
{
    if(m[k].lazy!=-1)   //这个位置要下传标记,不知道为什么,我觉得查询的时候下传标记就可以了
        pushdown(k);
    if(m[k].l==l&&m[k].r==r) //找到了全部元素都要修改的区间
    {
        m[k].sum=x;    //更新该区间的sum
        m[k].lazy=x;       //懒惰标记叠加
        return ;
    }
    int mid=(m[k].l+m[k].r)/2;
    if(r<=mid)  //被修改的区间完全在左区间
        changeSegment(2*k,l,r,x);
    else if(l>mid)
        changeSegment(2*k+1,l,r,x);
    else    //如果都不在,就要把区间分解成2块,分别在左右区间递归
    {
        changeSegment(2*k,l,mid,x);
        changeSegment(2*k+1,mid+1,r,x);
    }
    update(k);  //记得更新节点k
}

//区间查询
void query(int k,int l,int r)    //当前节点为k,查询区间[l,r]
{
    if(m[k].sum!=-1||m[k].l==m[k].r)
    {
        if(p[m[k].sum]==0)
        {
            p[m[k].sum]=1;
            ans++;
        }
        return ;
    }
    int mid=(m[k].l+m[k].r)/2;
    if(r<=mid)  //查询区间被包含在左子区间
        query(k*2,l,r);
    else if(l>mid)
        query(k*2+1,l,r);
    else
    {
        query(2*k,l,mid);
        query(2*k+1,mid+1,r);
    }
    //询问区间跨越2个子区间
}



int main()
{
    int n,m,a,b,c,o;
    char s[10];
    while(scanf("%d%d%d",&n,&o,&m)==3)
    {
        build(1,1,n);
        while(m--)
        {
            scanf("%s",s);
            if(s[0]=='C')
            {
                scanf("%d%d%d",&a,&b,&c);
                if(a>b)
                    swap(a,b);
                changeSegment(1,a,b,c);
            }
            else
            {
                scanf("%d%d",&a,&b);
                if(a>b)
                    swap(a,b);
                memset(p,0,sizeof(p));
                ans=0;
                query(1,a,b);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值