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)