题意:
就是一堆石头,开始全是一种颜色;
之后叫你染色,颜色不超过30种;
染色的规则是一段一段染;
叫你求某一段区间的颜色种数;
理解:
这题一看就是线段树;
主要就是记录颜色的问题;
开始我以为是线段树区间合并的问题;
结果经过师兄师姐的熏陶;
发现果然是状态压缩;
就是用数字记录颜色的种数;
因为不超过30种,所以用二进制每一位去记录颜色是一种很好的方法;
而且合并就是相或(|);
这样就不是区间合并了;
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
ll tree[500000];
int up[500000];
ll build(int l, int r, int rx)
{
if (l == r) return tree[rx] = 1 << 1;
return tree[rx]
= build(l, (l + r) / 2, rx * 2)
| build((l + r) / 2 + 1, r, rx * 2 + 1);
}
void push_down(int l, int r, int rx)
{
if (l != r)
{
up[rx * 2] = up[rx * 2 + 1] = up[rx];
tree[rx * 2] = tree[rx * 2 + 1] = (ll)1 << up[rx];
}
up[rx] = 0;
}
ll update(int l, int r, int L, int R, int rx, int t)
{
if (L > r || R < l) return tree[rx];
if (L >= l && R <= r)
{
up[rx] = t;
return tree[rx] = (ll)1 << t;
}
if (up[rx])
{
push_down(L, R, rx);
}
return tree[rx]
= update(l, r, L, (L + R) / 2, rx * 2, t)
| update(l, r, (L + R) / 2 + 1, R, rx * 2 + 1, t);
}
ll query(int l, int r, int L, int R, int rx)
{
if (L > r || R < l) return 0;
if (L >= l && R <= r)
{
return tree[rx];
}
if (up[rx])
{
push_down(L, R, rx);
}
return query(l, r, L, (L + R) / 2, rx * 2)
| query(l, r, (L + R) / 2 + 1, R, rx * 2 + 1);
}
int main()
{
int L, T, O;
scanf("%d%d%d", &L, &T, &O);
build(1, L, 1);
for (int i = 0; i < O; ++i)
{
getchar();
char ch;
scanf("%c", &ch);
if (ch == 'C')
{
int l, r, t;
scanf("%d%d%d", &l, &r, &t);
if (r < l) l ^= r ^= l ^= r; //最坑的是输入竟然可以相反。。。。所以要交换啊
update(l, r, 1, L, 1, t);
//for (int i = 0; i < 4 * L + 5; ++i) printf("%lld ", tree[i]); printf("\n");
}
else
{
int l, r, ans = 0;
scanf("%d%d", &l, &r);
if (r < l) l ^= r ^= l ^= r;
ll num = query(l, r, 1, L, 1);
//for (int i = 0; i < 4 * L + 5; ++i) printf("%lld ", tree[i]); printf("\n%lld\n", num);
for (int i = 0; i < 33; ++i)
{
if ((ll)1 << i & num)
++ans;
}
printf("%d\n", ans);
}
}
return 0;
}