题意
n n n( n n n<=10000) 个人依次贴海报,给出每张海报所贴的范围 l i l_i li, r i r_i ri(1<= l i l_i li<= r i r_i ri<=10000000) 。求出最后还能看见多少张海报。
输入描述
第一行: 样例个数 T T T
对于各样例
- 第一行: 贴海报的人 n n n
- 接下来 n n n行: 每个人贴海报的范围
输出描述
各样例一行,输出可以看见的海报个数
思路
使用线段树储存每个海报的范围,线段树的精髓就是能使用根节点就不使用叶子节点,减少下探的次数,减小算法复杂度,所以使用 l a z y − t a g lazy-tag lazy−tag。
Accepted code
/*
* @Autor: CofDoria
* @Date: 2020-11-08 10:07:01
* @LastEditTime: 2020-11-09 16:06:21
*/
// 二叉线段树 离散化 涂色
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <set>
#include <vector>
using namespace std;
#define maxn 200005
int T, n;
int a, b;
int right;
int idx[200005];
int lztg[4 * maxn]; // lazy-tag
set<int> li;
map<int, int> mp;
struct node { // 线段树结构体
int l, r, c;
} t[4 * maxn];
struct edge { // 存海报的边
int l, r;
} eg[100005];
void pushdown(int u) {
t[u << 1].c = t[(u << 1) + 1].c = t[u].c;
lztg[u << 1] = lztg[(u << 1) + 1] = 1;
}
void update(int lef, int rig, int l, int r, int u, int c) {
if (lef <= l && rig >= r) {
t[u].c = c;
lztg[u] = 1;
return;
}
if (l > rig || r < lef) return;
if (lztg[u] == 1) pushdown(u); // 如果这个节点的lazy-tag被标记为涂色
lztg[u] = 0; // pushdown标记左右孩子的lazy-tag为涂色
int mid = (r + l) >> 1;
update(lef, rig, l, mid, u << 1, c);
update(lef, rig, mid + 1, r, (u << 1) + 1, c);
}
void query(int u) {
if (lztg[u]) {
if (lztg[u] == 1) li.insert(t[u].c);
return;
}
query(u << 1);
query((u << 1) + 1);
return;
}
int main() {
scanf("%d", &T);
while (T--) {
memset(lztg, -1, sizeof(lztg));
mp.clear();
li.clear();
scanf("%d", &n);
int tot = 0;
for (int i = 0; i < n; i++) {
scanf("%d%d", &eg[i].l, &eg[i].r);
idx[tot++] = eg[i].l;
idx[tot++] = eg[i].r;
}
sort(idx, idx + tot);
right = unique(idx, idx + tot) - idx; // 排序去重
for (int i = 0; i < right; i++) mp[idx[i]] = i + 1; //转换下标
for (int i = 0; i < n; i++)
update(mp[eg[i].l], mp[eg[i].r], 1, right, 1, i + 1);
query(1);
printf("%d\n", li.size());
}
}