题意:长度为n的数组,每个点的颜色开始都是2,然后q次操作,操作1是把[L,R] 区间的颜色变成某个值C, 第二种是查询[L,R] 区间存在哪些颜色,并把颜色的编号输出( 按照字典序).(N<=1e6, M<=1e5, C<=30)
思路:因为C<=30,所以我们可以把颜色用二进制来吃存储,线段树每个节点的二进制表示其区间存在的颜色。这样就成了简单的区间更新和查询了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int tree[maxn*4], lazy[maxn*4];
int n, m;
void push_up(int root)
{
tree[root] = tree[root*2] | tree[root*2+1];
}
void push_down(int root, int l, int r)
{
if(lazy[root])
{
tree[root*2] = tree[root*2+1] = 1<<(lazy[root]-1);
lazy[root*2] = lazy[root*2+1] = lazy[root];
lazy[root] = 0;
}
}
void build(int root, int l, int r)
{
if(l == r)
{
tree[root] = 2;
lazy[root] = 0;
return ;
}
int mid = (l+r)/2;
build(root*2, l, mid);
build(root*2+1, mid+1, r);
push_up(root);
}
void update(int root, int l, int r, int i, int j, int val)
{
if(i <= l && j >= r)
{
tree[root] = 1<<(val-1);
lazy[root] = val;
return ;
}
push_down(root, l, r);
int mid = (l+r)/2;
if(i <= mid) update(root*2, l, mid, i, j, val);
if(j > mid) update(root*2+1, mid+1, r, i, j, val);
push_up(root);
}
int query(int root, int l, int r, int i, int j)
{
if(i <= l && j >= r) return tree[root];
push_down(root, l, r);
int tmp = 0;
int mid = (l+r)/2;
if(i <= mid) tmp |= query(root*2, l, mid, i, j);
if(j > mid) tmp |= query(root*2+1, mid+1, r, i, j);
return tmp;
}
int main(void)
{
while(cin >> n >> m)
{
if(n == 0 && m == 0) break;
memset(lazy, 0, sizeof(lazy));
memset(tree, 0, sizeof(tree));
build(1, 1, n);
for(int i = 1; i <= m; i++)
{
char cmd;
int x, y, z;
scanf(" %c %d%d", &cmd, &x, &y);
if(cmd == 'P')
{
scanf("%d", &z);
update(1, 1, n, x, y, z);
}
else
{
int ans = query(1, 1, n, x, y);
int flag = 0;
for(int i = 1; i <= 30; i++)
{
if(ans&(1<<(i-1)))
{
if(flag) printf(" ");
printf("%d", i);
flag++;
}
}
puts("");
}
}
}
return 0;
}