至多有30种颜色,要求某个区间段有多少种不同的颜色,很容易想到用1个位表示一种颜色,
最后二进制的或运算一下子就求出了颜色的种数。
跟poj 3468一样,要支持区间的修改和区间的查询,但我始终没有想到如何用zkw式的线段
树的解决办法,貌似这个题目只能自顶向下查询,所以只好用朴素的线段树,为了把更新操
作的复杂度变成O(log n),要引入一个标记,表示这条线段的所有子线段都被刷成了同一种颜色,
好在颜色用二进制表示的话,如果只有一种颜色,那么颜色值必定是2的n次幂,用 x&(x-1)
可以判断一个数是否为2的n次幂。所以颜色值也可以作为标记,每必要再引入一个标记了。
#include <stdio.h> //#define DEBUG #ifdef DEBUG #define debug(...) printf( __VA_ARGS__) #else #define debug(...) #endif struct tree_node { int s, t; int c; }; struct tree_node tree[262144]; int ans; int color[31]; /* 每种颜色用一个bit表示, 那么color[i]等于2的i次幂 */ /* 计算二进制中1的个数 */ int c1(int x) { x=(x & 0x55555555) + ((x >>1 ) & 0x55555555); x=(x & 0x33333333) + ((x >>2 ) & 0x33333333); x=(x & 0x0F0F0F0F) + ((x >>4 ) & 0x0F0F0F0F); x=(x & 0x00FF00FF) + ((x >>8 ) & 0x00FF00FF); x=(x & 0x0000FFFF) + ((x >>16) & 0x0000FFFF); return x; } /* 建造一棵完全二叉树 */ void build_tree(int n) { int i, M, left; for (M = 1; M < n; M <<= 1); for (i = 2*M-1; i > 0; i--) { if (i >= M) { /* 叶子节点 */ tree[i].s = tree[i].t = i-M+1; tree[i].c = 1; } else { /* 分支节点 */ left = (i<<1); tree[i].c = tree[left].c | tree[left+1].c; tree[i].s = tree[left].s; tree[i].t = tree[left+1].t; } } } /* 查询区间[s,t]的颜色种数 */ void query(int r, int s, int t) { int m, left; struct tree_node *node; node = tree + r; if (node->s == s && node->t == t) { /* 一条匹配线段找到 */ ans |= node->c; return; } m = (node->s + node->t)>>1; left = (r << 1); //只有一种颜色,说明是刚刷过的匹配线段,顺势把它的孩子也刷了 if ((node->c & (node->c-1)) == 0) { tree[left].c = tree[left+1].c = node->c; } if (s > m) { /* 查找右孩子 */ query(left+1, s, t); } else if (t <= m) { /* 查找左孩子 */ query(left, s, t); } else { /* 两个孩子都找 */ query(left, s, m); query(left+1, m+1, t); } } /* 把线段[s,t]刷成颜色c, 自底向上刷,没必要把区间内的每条线段 * 都刷,只要刷到匹配线段即可 */ void update(int r, int s, int t, int c) { int m, left; struct tree_node *node; node = tree + r; if (node->s == s && node->t == t) { /* 刷到匹配线段就停止 */ node->c = c; return; } left = (r << 1); m = (node->s + node->t)>>1; /* 由于只刷到匹配线段,匹配线段的孩子没有被刷,这个时候 * 要把它们的孩子刷成真正的颜色 */ if ((node->c & (node->c-1)) == 0) { tree[left].c = tree[left+1].c = node->c; } if (s > m) { update(left+1, s, t, c); } else if (t <= m) { update(left, s, t, c); } else { update(left, s, m, c); update(left+1, m+1, t, c); } //自底向上刷 node->c = (tree[left].c | tree[left+1].c); } int main() { int n, i, s, t, o, c; char action; for (i = 1; i < 32; i++) { color[i] = 1<<(i-1); } scanf("%d %*d %d", &n, &o); build_tree(n); while (o--) { getchar(); scanf("%c %d %d", &action, &s, &t); if (action == 'P') { ans = 0; if (s < t) { query(1, s, t); } else { query(1, t, s); } printf("%d\n", c1(ans)); } else { scanf("%d", &c); if (s < t) { update(1, s, t, color[c]); } else { update(1, t, s, color[c]); } } } return 0; }