线段树染色问题 (Luogu P1558 色板游戏 Solution)

Luogu 1558 色板游戏

这是线段树中很基础的染色问题,像我等(JuRuo)第一秒肯定是30棵大树建好,用线段树维护一下

这样既快速又简洁

但是这好像没有丝毫乐趣,感受不到蓝题的感觉,不把三十棵树合成就不爽,接下来接着思考如何优化空间(重点)


题目中有要求颜料种类\(T\)的范围在\(1-30\)间,要压空间的话就用压位

那么就若是一位一个颜色,那么一个\(int\)刚好够,将二进制下的第\(i\)位表是该线段中是否存在第\(i\)个颜色

接下来就是如何维护的问题了

\(step \ 1\) 建树

把每个叶子节点都赋值为1<<1,表示存在第一种颜色

push_up中的运算符改为or或是|

不会位运算的点这里

\(code\)

inline void push_up(int p) {
    t[p] = t[lc(p)] | t[rc(p)];
}
inline void build(int p,int l,int r) {
    tag[p] = 0;
    if(l == r) {
        t[p] = 1<<1;
        return ;
    }
    int mid = (l+r)>>1;
    build(lc(p),l,mid);
    build(rc(p),mid+1,r);
    push_up(p);
}

\(step \ 2\) 更新

说两个易错点

一、因为是覆盖颜色,所以只要对tag或者节点之间赋值就行了

二、tag==0时千万不要push_down

\(code\)

inline void f(int p,int k) {
    tag[p] = k;
    t[p] = k;
}
inline void push_down(int p) {
    if(!tag[p]) return ;
    f(lc(p),tag[p]);
    f(rc(p),tag[p]);
    tag[p] = 0;
}
inline void updata(int p,int l,int r,int x,int nl,int nr) {  //变量说明(从左向右):现在节点编号 需要修改的左右端点 修改后的颜色 现在编号所对应的线段的左右端点
    if(nr<l||nl>r) return ;
    if(l<=nl&&nr<=r) {
        t[p] = (1<<x);
        tag[p] = (1<<x);
        return ;
    }
    push_down(p);
    int mid = (nl+nr)>>1;
    if(mid>=l) updata(lc(p),l,r,x,nl,mid);
    if(mid<r)  updata(rc(p),l,r,x,mid+1,nr);
    push_up(p);
}

\(step\ 3\) 求值

这里就没什么要点了,毕竟上面都提过了

\(code\)

inline int query(int p,int l,int r,int nl,int nr) {  //变量的含义同上
    if(nr<l||nl>r) return 0;
    if(l<=nl&&nr<=r) return t[p];
    push_down(p);
    int mid = (nl+nr)>>1;
    int ans = 0;
    if(mid>=l) ans |= query(lc(p),l,r,nl,mid);
    if(mid<r)  ans |= query(rc(p),l,r,mid+1,nr);
    return ans;
}

\(step\ 4\) 处理数据

本(JuRuo)有喜欢这种方式方式

for(int i=n;i;i-=lowbit(i)) ans++;

其中n为从线段树中所取得的结果,ans为最终答案

不知道lowbit什么意思的请右转baidu

\(step\ 5\) 细节处理

直接引入题目原话吧(这里 A, B, C 为整数, 可能A> B,这样的话需要你交换A和B)

猜你喜欢

转载自www.cnblogs.com/Ax-Dea/p/12188610.html