题解
2025-10-04 17:06:12
发布于:广东
2阅读
0回复
0点赞
这是一道非常好的题,值得思考。题目要求维护区间内颜色的种数,那该如何表示一个区间内颜色的种数呢?
其实我们没有必要维护区间内颜色的种数,而是维护这种颜色是否出现过。由于只有出现过和没出现两种状态,所以可以用状态压缩,用一个位二进制数来表示,从右往左第位为表示在此区间颜色出现过,反之没有出现过;
接下来是修改和查询操作:
修改:完全包含时直接设为,即,充分利用了位运算的优势;若未完全包含,则使用位或运算,这样就不会重复计数,即将设为
查询:同样道理,左儿子(如果有)异或右儿子(如果有)
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,t;
class segtree
{
private:
vector<int> tree,tag; // tree存储颜色集合(位运算), tag存储懒标记(颜色编号)
public:
inline void build()
{
tree.resize(n<<2|1,1),tag.resize(n<<2|1,0); // 初始化树和标记,初始颜色为1(1<<0)
}
inline void pushdown(int id)
{
if (tag[id]==0) return; // 没有标记则返回
tree[id<<1]=1<<(tag[id]-1),tree[id<<1|1]=1<<(tag[id]-1); // 将颜色标记下传给左右儿子
tag[id<<1]=tag[id],tag[id<<1|1]=tag[id]; // 设置左右儿子的懒标记
tag[id]=0; // 清空当前标记
}
inline void update(int l,int r,int ql,int qr,int v,int id)
{
if (ql<=l && qr>=r) // 完全覆盖区间
{
tree[id]=1<<(v-1),tag[id]=v; // 设置当前节点颜色和标记
return;
}
pushdown(id); // 下传标记
int mid=(l+r)>>1;
if (ql<=mid) update(l,mid,ql,qr,v,id<<1); // 更新左子树
if (qr>mid) update(mid+1,r,ql,qr,v,id<<1|1); // 更新右子树
tree[id]=tree[id<<1]|tree[(id<<1)|1]; // 合并左右子树的颜色集合
}
inline int query(int l,int r,int ql,int qr,int id)
{
if (ql<=l && qr>=r) // 完全覆盖区间
return tree[id]; // 直接返回颜色集合
pushdown(id); // 下传标记
int mid=(l+r)>>1,sum=0;
if (ql<=mid) sum|=query(l,mid,ql,qr,id<<1); // 查询左子树
if (qr>mid) sum|=query(mid+1,r,ql,qr,id<<1|1); // 查询右子树
return sum; // 返回合并后的颜色集合
}
};
int f(int x)
{
int i=0;
for (;x>0;x&=(x-1),i++); // 计算二进制中1的个数(颜色种类数)
return i;
}
signed main()
{
cin >> n >> t >> m; // 读入长度n, 颜色总数t, 操作数m
segtree tree;
tree.build(); // 初始化线段树
char c;
int x,y,z;
while (m--)
{
cin >> c >> x >> y;
if (x>y) swap(x,y); // 确保区间左端点<=右端点
if (c=='C') // 涂色操作
{
cin >> z;
tree.update(1,n,x,y,z,1); // 更新区间颜色
}
else // 查询操作
cout << f(tree.query(1,n,x,y,1)) << endl; // 查询并输出颜色种类数
}
return 0;
}
这里空空如也





有帮助,赞一个