题意

你有一个无限大的绘图板,开始颜色是\(0\) , 你将进行\(n\) 次绘图,第\(i\) 次绘图会将左下角为 \((x_1, y_1)\),右上角为\((x_2, y_2)\) 的矩形涂成颜色\(i\). 问你最后能看到的颜色数量 ( 包括 0 ).

\(n\le 10^5\)

分析

  • \(kd-tree\) 不好维护可行面积,所以考虑扫描线套线段树。

  • 我们可以给每种颜色一个权值(出现的时间)表示他被看到的难易程度,出现时间越晚的颜色越容易被看到。

  • 线段树上维护当前可以被看到的未出现的颜色中权值最大的颜色 \(Max\),每次取出根节点维护的值并打上标记,表示已经看到过。

  • 但是有可能 \(Max\) 被当前区间的某个已经出现的颜色全部遮住了,所以还需要维护标记 \(Min\) ,意义如下:

    我们记 \(mx_p\) 表示所有覆盖位置 \(p\) 的已出现权值的最大值, \(Min=\min_\limits{p\in[l,r]}\{mx_p\}\) 。

  • 再用一个 \(set​\) 维护出现在整个区间中的最大的颜色是多少,显然其他的颜色不是被他覆盖就是不能覆盖它。

  • 节点维护:

    \(Max=max({Max}_l,{Max}_r,maxv)\) ,\(maxv\) 表示出现在 \(set\) 中的最大权值(如果还没有被看到)

    \(Min=max(min({Min}_l,{Min}_r),maxv)\) ,\(maxv\) 表示出现在 \(set​\) 中的最大权值(如果已经出现了)

  • 所以如果区间的 \(Max < Min\) 表示不存在未出现的颜色可以被看到。

  • 总时间复杂度为 \(O(nlog^2n)\)。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
#define re(x) memset(x, 0, sizeof x)
inline int gi() {
int x = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
return x * f;
}
template <typename T> inline void Max(T &a, T b){if(a < b) a = b;}
template <typename T> inline void Min(T &a, T b){if(a > b) a = b;}
const int N = 2e5 + 7;
int n, ndc, vc;
int V[N], mx[N << 2], mi[N << 2], xl[N], xr[N], yl[N], yr[N];
bool vis[N];
#define Ls o << 1
#define Rs o << 1 | 1
struct qs {
int p, l, r, opt;
bool operator <(const qs &rhs) const {
if(p != rhs.p) return p < rhs.p;
return opt < rhs.opt;
}
}q[N];
struct Heap {
priority_queue<int>A, B;
void ins(int x){ A.push(x);}
void del(int x){ B.push(x);}
int top() {
while(!B.empty() && A.top() == B.top()) A.pop(), B.pop();
return A.empty() ? 0 : A.top();
}
}col[N << 2];
void pushup(int l, int r, int o) {
if(l ^ r) {
mi[o] = min(mi[Ls], mi[Rs]);
mx[o] = max(mx[Ls], mx[Rs]);
}else mi[o] = mx[o] = 0;
if(col[o].top()) {
int x = col[o].top();
if(vis[x]) Max(mi[o], x);
else Max(mx[o], x);
if(mi[o] > mx[o]) mx[o] = 0;
}
}
void modify(int L, int R, int l, int r, int o, int v) {
if(L <= l && r <= R) {
if(v > 0) col[o].ins(v);
if(v < 0) col[o].del(-v);
pushup(l, r, o);
return;
}int mid = l + r >> 1;
if(L <= mid) modify(L, R, l, mid, Ls, v);
if(R > mid) modify(L, R, mid + 1, r, Rs, v);
pushup(l, r, o);
}
int main() {
n = gi();
rep(i, 1, n) {
xl[i] = gi(), yl[i] = gi(), xr[i] = gi(), yr[i] = gi();
q[++ndc] = (qs){ xl[i], yl[i], yr[i] - 1, i};
q[++ndc] = (qs){ xr[i], yl[i], yr[i] - 1, -i};
V[++vc] = yl[i];
V[++vc] = yr[i];
}
sort(V + 1, V + 1 + vc);
sort(q + 1, q + 1 + ndc);
vc = unique(V + 1, V + 1 + vc) - V - 1;
rep(i, 1, n) {
yl[i] = lower_bound(V + 1, V + 1 + vc, yl[i]) - V;
yr[i] = upper_bound(V + 1, V + 1 + vc, yr[i] - 1) - V - 1;
}
rep(i, 1, ndc) {
q[i].l = lower_bound(V + 1, V + 1 + vc, q[i].l) - V;
q[i].r = upper_bound(V + 1, V + 1 + vc, q[i].r) - V - 1;
}
for(int i = 1, j = 1; i <= ndc; i = j) {
for(;j <= ndc && q[j].p == q[i].p; ++j)
modify(q[j].l, q[j].r, 1, vc, 1, q[j].opt);
while(mx[1]) {
int x = mx[1];vis[x] = 1;
modify(yl[x], yr[x], 1, vc, 1, 0);
}
}
int ans = 1;
rep(i, 1, n) if(vis[i]) ++ans;
printf("%d\n", ans);
return 0;
}

[CF983D]Arkady and Rectangles[线段树+可删堆/set]的更多相关文章

  1. 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp

    题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...

  2. Codeforces 524E Rooks and Rectangles 线段树

    区域安全的check方法就是, 每行都有哨兵或者每列都有哨兵,然后我们用y建线段树, 维护在每个y上的哨兵的x的最值就好啦. #include<bits/stdc++.h> #define ...

  3. [CF983D]Arkady and Rectangles

    题意:按顺序在坐标轴上画$n$个颜色为$1\cdots n$的矩形(数字大的颜色覆盖数字小的颜色),问最后能看到多少种颜色 先离散化,然后考虑扫描线+线段树 线段树每个节点用一个set存覆盖整个区间的 ...

  4. Codeforces 1163F 最短路 + 线段树 (删边最短路)

    题意:给你一张无向图,有若干次操作,每次操作会修改一条边的边权,每次修改后输出1到n的最短路.修改相互独立. 思路:我们先以起点和终点为根,找出最短路径树,现在有两种情况: 1:修改的边不是1到n的最 ...

  5. 【BZOJ 2333 】[SCOI2011]棘手的操作(离线+线段树|可并堆-左偏树)

    2333: [SCOI2011]棘手的操作 Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边 ...

  6. Vijos 1404 遭遇战 - 动态规划 - 线段树 - 最短路 - 堆

    背景 你知道吗,SQ Class的人都很喜欢打CS.(不知道CS是什么的人不用参加这次比赛). 描述 今天,他们在打一张叫DUSTII的地图,万恶的恐怖分子要炸掉藏在A区的SQC论坛服务器!我们SQC ...

  7. 【递归】【线段树】【堆】AtCoder Regular Contest 080 E - Young Maids

    给你一个1~n的排列p,n是偶数,每次从中任选一对相邻的数出来,插到排列q的开头,如此循环,问你所能得到的字典序最小的排列q. 我们先确定q开头的两个数q1,q2,q1一定是p的奇数位的最小的数,而q ...

  8. VK Cup 2015 - Round 1 E. Rooks and Rectangles 线段树 定点修改,区间最小值

    E. Rooks and Rectangles Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.com/problemse ...

  9. hdu2461 Rectangles 线段树--扫描线

    You are developing a software for painting rectangles on the screen. The software supports drawing s ...

随机推荐

  1. JAVA将list转化为xml文件

    pojo类: public class TreeNode { private int id; private String nodeId; private String parentId; priva ...

  2. RBAC用户权限管理数据库设计【转载】

    本文转载自:https://www.kancloud.cn/martist/ma_zhao_liu/374123 简单地说,一个用户拥有若干角色,每一个角色拥有若干权限.这样,就构造成“用户-角色-权 ...

  3. post请求的四种数据格式

    1.application/json:这是最常见的 json 格式:{"input1":"xxx","input2":"ooo&q ...

  4. sysbench使用

      1 部署 1.1 官方主页 https://github.com/Percona-Lab/sysbench-tpcc https://github.com/akopytov/sysbench 1. ...

  5. 参数innodb_force_recovery影响了整个InnoDB存储引擎的恢复状况

    参数innodb_force_recovery影响了整个InnoDB存储引擎的恢复状况.该值默认为0,表示当需要恢复时执行所有的恢复操作.当不能进行有效恢复时,如数据页发生了corruption,My ...

  6. 从零起步做到Linux运维经理,你必须管好的23个细节

    不想成为将军的士兵,不是好士兵-拿破仑 如何成为运维经理?成为运维经理需要什么样的能力?我想很多运维工程师都会有这样的思考和问题. 如何成为运维经理.一般来说,运维经理大概有两种出身,一种是从底层最基 ...

  7. SqlServer中sqlmaint 实用工具和xp_sqlmaint扩展过程

    sqlmaint 实用工具可以对一个或多个数据库执行一组指定的维护操作.使用 sqlmaint,可以运行 DBCC 检查.备份数据库及其事务日志.更新统计以及重建索引.所有数据库维护活动都会生成报表, ...

  8. Huawei DHCP 中继配置实例

    配置DHCP中继示例 组网需求 如图1,DHCP客户端所在的网段为10.100.0.0/16,而DHCP服务器所在的网段为202.40.0.0/16.需要通过带DHCP中继功能的设备中继DHCP报文, ...

  9. UNIX高级环境编程(15)进程和内存分配 < 故宫角楼 >

    故宫角楼是很多摄影爱好者常去的地方,夕阳余辉下的故宫角楼平静而安详.   首先,了解一下进程的基本概念,进程在内存中布局和内容. 此外,还需要知道运行时是如何为动态数据结构(如链表和二叉树)分配额外内 ...

  10. sha256sum和 md5sum 命令之间的区别

    Short answer: For verifying ISOs, there is no practical difference, use whichever you want, as long ...