题目描述
给定一个 (n) 项的数列,初始颜色都为 (1),有 (m) 次操作,(T) 种颜色,分为 (2) 种:
(space space space space space space) 把区间 ([a, space b]) 改为一个颜色 (c)
$space space space space space space $ 询问区间 ([a, space b]) 有多少不同的颜色
数据范围
(1 leq n leq 10^5, space 1 leq T leq 30, space 1 leq m leq 10^5)
Solution
想到最暴力的做法:对于每种颜色维护一棵线段树,就有 (3) 种操作:区间推平、区间清空和区间求和(其实也可以不写求和)
考虑标记 (2) 个 (mathrm{tag}) 来分别维护区间推平和区间清空,在下压的时候还应该考虑两个 (mathrm{tag}) 获得的先后顺序。因此我们需要记录每个 (mathrm{tag}) 获得的时间戳,用来判断到底该下压哪个 (mathrm{tag})
(mathrm{push space down}) 操作时一定要注意,两个 (mathrm{tag}) 一起清空,两个儿子的时间戳和要下放的时间戳 取 (mathrm{max}).
总复杂度 (mathrm{O(Tm log_n)})
Code
因为博主太菜了,感觉本题及其卡时间和空间。吸氧 (+ space C^{++}11) 才能跑过。
([) 题外话 (]) 极其悲惨的评测记录(这还只是一部分)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define re register
using namespace std;
const int N = 100010;
int n, T, O, sum[31][N << 2], tag[31][N << 2], clear[31][N << 2];
void push_up(int x, int k) { sum[k][x] = sum[k][x << 1] + sum[k][x << 1 | 1]; }
void push_down(int x, int l, int r, int k)
{
int mid = (l + r) >> 1, t = tag[k][x], c = clear[k][x];
if(!t && !c) return ;
if(t < c)
{
clear[k][x << 1] = max(clear[k][x << 1], c);
clear[k][x << 1 | 1] = max(clear[k][x << 1 | 1], c);
sum[k][x << 1] = sum[k][x << 1 | 1] = 0;
}
else
{
tag[k][x << 1] = max(tag[k][x << 1], t);
tag[k][x << 1 | 1] = max(tag[k][x << 1 | 1], t);
sum[k][x << 1] = mid - l + 1;
sum[k][x << 1 | 1] = r - mid;
}
clear[k][x] = tag[k][x] = 0;
return ;
}
void add(int x, int l, int r, int stdl, int stdr, int k, int tim)
{
if(stdl <= l && stdr >= r)
{
tag[k][x] = tim, sum[k][x] = r - l + 1;
push_down(x, l, r, k);
return ;
}
int mid = (l + r) >> 1;
push_down(x, l, r, k);
if(stdl <= mid) add(x << 1, l, mid, stdl, stdr, k, tim);
if(stdr > mid) add(x << 1 | 1, mid + 1, r, stdl, stdr, k, tim);
push_down(x, l, r, k), push_up(x, k);
}
void del(int x, int l, int r, int stdl, int stdr, int k, int tim)
{
if(stdl <= l && stdr >= r)
{
clear[k][x] = tim, sum[k][x] = 0;
push_down(x, l, r, k);
return ;
}
int mid = (l + r) >> 1;
push_down(x, l, r, k);
if(stdl <= mid) del(x << 1, l, mid, stdl, stdr, k, tim);
if(stdr > mid) del(x << 1 | 1, mid + 1, r, stdl, stdr, k, tim);
push_down(x, l, r, k), push_up(x, k);
}
int query(int x, int l, int r, int stdl, int stdr, int k)
{
if(l > stdr || r < stdl) return 0;
if(stdl <= l && stdr >= r) return sum[k][x];
int mid = (l + r) >> 1;
push_down(x, l, r, k);
return query(x << 1, l, mid, stdl, stdr, k) +
query(x << 1 | 1, mid + 1, r, stdl, stdr, k);
push_down(x, l, r, k), push_up(x, k);
}
char opt;
int a, b, c, cnt = 1;
inline int read()
{
int x = 0; char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x;
}
int main()
{
n = read(), T = read(), O = read();
tag[1][1] = 1, sum[1][1] = n;
while(O--)
{
cin >> opt;
if(opt == 'C')
{
a = read(), b = read(), c = read();
if(a > b) swap(a, b);
for(re int i = 1; i <= T; i++)
cnt++, del(1, 1, n, a, b, i, cnt);
cnt++, add(1, 1, n, a, b, c, cnt);
}
else
{
a = read(); b = read();
if(a > b) swap(a, b);
int res = 0;
for(re int i = 1; i <= T; i++)
if(query(1, 1, n, a, b, i) > 0) res++;
printf("%d
", res);
}
}
return 0;
}