色板游戏

题目链接

题目描述

给定一个 \(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\n", res);
        }
    }
    return 0;
}
posted @ 2020-11-30 07:17  Nyxia  阅读(167)  评论(0编辑  收藏  举报