ZOJ 1610「Count the Colors」:在數軸上依次繪製彩色線段,後繪製覆蓋前者。最終統計每種可見顏色的線段數(連續同色算一段)。本文解析線段樹區間賦值懶標記 + 最終遍歷統計的標準解法。
[x1, x2) 顏色 c(注意右端點開區間)。color,-1 表示混合色,≥0 表示純色。tag,-1 表示無,≥0 表示整段染為該色。void pushdown(int rt) {
if (tag[rt] != -1) {
tag[ls] = tag[rs] = tag[rt];
color[ls] = color[rs] = tag[rt];
tag[rt] = -1;
}
}
void update(int L, int R, int c, int l, int r, int rt) {
if (L <= l && r <= R) {
color[rt] = tag[rt] = c;
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
if (L <= mid) update(L, R, c, l, mid, ls);
if (R > mid) update(L, R, c, mid+1, r, rs);
// 內部節點不維護 color,僅葉子節點最終統計用
}
中序遍歷線段樹葉子節點,收集顏色序列 col[],然後掃描統計連續段:
void collect(int l, int r, int rt) {
if (l == r) {
col[cnt++] = color[rt];
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
collect(l, mid, ls);
collect(mid+1, r, rs);
}
// 統計
for (int i = 0; i < cnt; ) {
int c = col[i];
if (c != -1) ans[c]++;
int j = i + 1;
while (j < cnt && col[j] == c) ++j;
i = j;
}
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXC = 8000;
const int MAXN = MAXC * 4 + 10;
int color[MAXN], tag[MAXN];
int col[MAXN], ans[MAXN];
int cnt, n;
void pushdown(int rt) { ... }
void update(int L, int R, int c, int l, int r, int rt) { ... }
void collect(int l, int r, int rt) { ... }
int main() {
while (scanf("%d", &n) == 1) {
cnt = 0;
memset(tag, -1, sizeof(tag));
memset(color, -1, sizeof(color));
memset(ans, 0, sizeof(ans));
for (int i = 0; i < n; ++i) {
int x1, x2, c;
scanf("%d%d%d", &x1, &x2, &c);
update(x1, x2-1, c, 0, MAXC, 1); // 右端點開區間
}
collect(0, MAXC, 1);
for (int i = 0; i < cnt; ) {
int c = col[i];
if (c != -1) ans[c]++;
int j = i + 1;
while (j < cnt && col[j] == c) ++j;
i = j;
}
for (int c = 0; c <= 8000; ++c)
if (ans[c]) printf("%d %d\n", c, ans[c]);
puts("");
}
return 0;
}
x1 x2 c 表示 [x1, x2),更新時用 x2-1。本題展示了「區間賦值懶標記線段樹 + 最終葉子遍歷統計」的經典模式。關鍵在於:
此模板適用於所有「區間覆蓋 + 最終統計顏色/值分布」問題。