Mayor‘s posters(线段树染色)

题目链接: Mayor’s posters


2023.4.13 更新了代码, 修复了错误的离散化长度, 已在代码中注出.


大致题意

有n个人依次贴海报, 第i个海报的范围是[li, ri]. 后面贴的海报会覆盖掉之前贴的海报.

问: 最终还能看到多少张海报.

解题思路

本题属于线段树维护区间染色类别的题目. 本题考察线段树的区间修改, 根节点查询.

这类题目的一大特点就是: 线段树内维护当前整个区间的值, 相当于当前区间维护的值是一个懒标记, 其子节点的值也都为当前节点的值, 若当前区间内部的子区间值不统一, 则需要递归分别给每个子区间赋值, 此时认为当前节点认为没有值即可, 这样在查询的时候, 如果访问到一整个区间都是相同值, 则直接返回即可, 反之需要递归处理.

特别的, 本题的实际数据范围推荐为1E5, 题目中的1E4会报RE

AC代码

#include <iostream>
#include <vector>
#include <cstdio>
#include <algorithm>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
bool vis[N]; //标记是否已经出现过第i张海报
struct node {
    int l, r;
    int id; //id表示lazy标签, 也表示当前节点值. 
}t[N << 2];
vector<int> v; vector<pair<int, int> > area; //v为离散化数组, area存放海报区间
int find(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); }

void pushdown(node& op, int id) { op.id = id; }
void pushdown(int x) {
    if (!t[x].id) return;
    pushdown(t[x << 1], t[x].id), pushdown(t[x << 1 | 1], t[x].id);
    t[x].id = 0; 
}
//因为线段树内部不维护任何数值, 所以也可以省去pushup这一操作.

void build(int l, int r, int x = 1) {
    t[x] = { l, r, 0 }; //id: 特别的, 如果0也是染色的点, 那么应初始化为-1
    if (l == r) return;
    int mid = l + r >> 1;
    build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}

void modify(int l, int r, int c, int x = 1) {
    if (l <= t[x].l && r >= t[x].r) { pushdown(t[x], c); return; }
    pushdown(x);
    int mid = t[x].l + t[x].r >> 1;
    if (l <= mid) modify(l, r, c, x << 1);
    if (r > mid) modify(l, r, c, x << 1 | 1);
}

int ask(int x = 1) { 
    if (t[x].id) { //当前子树均为同一值, 没必要再递归下去了
        if (vis[t[x].id]) return 0;
        return vis[t[x].id] = 1;
    }
	if (t[x].l == t[x].r) return 0; //到叶子结点一定要结束递归
    
    return ask(x << 1) + ask(x << 1 | 1);
}

int main()
{
    int T; cin >> T; 
    while (T--) {
        v.clear(); v.push_back(-0x3f3f3f3f); //这里是为了离散化下标从1开始
        area.clear();
        int n; scanf("%d", &n);
        rep(i, n) {
            vis[i] = 0; //顺带初始化vis
            int l, r; scanf("%d %d", &l, &r);
            v.push_back(l), v.push_back(r);
            area.push_back({ l, r });
        }
        
        /* 离散化部分 */
        sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());
       
        /* 2023.4.13更新代码 BEGIN */
        int vlen = v.size();
   		rep(i, vlen - 1) if (v[i] - v[i - 1] != 1) v.push_back(v[i] - 1); //记为*

   		// 下面这行是之前错误的代码, 我们的离散化判别应扫描整个vector, 而不是只判断到n
   		// rep(i, n) if (v[i] - v[i - 1] != 1) v.push_back(v[i] - 1); //记为*
   		/* 2023.4.13更新代码 END */
   		
        sort(v.begin(), v.end());
        
        build(1, v.size() - 1); //因为我的v数组有个-INF, 所以实际的建树大小应为v.size()-1
        
        for (int i = 0; i < n; ++i) {
            int l = area[i].first, r = area[i].second;
            l = find(l), r = find(r);
            modify(l, r, i + 1); //个人习惯编号从1开始
        }
        printf("%d\n", ask());
    }
    return 0;
}

这里关于*部分加以解释: 这里是为了防止离散化之前区间不连续, 而错误的离散化导致离散化后区间连续的情况. 网上已经有很多大佬用大篇文章及样例说明过了, 这里就不再多赘述啦, 毕竟我也是刚学过来的. 向前辈们学习致敬!

kuangbin线段树专题点这里!!!

END

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值