题意:
那个城市里要竞选市长,然后在一块墙上可以贴海报为自己拉票,每个人可以贴连续的一块区域,后来帖的可以覆盖前面的,问到最后一共可以看到多少张海报。
分析:
线段树的区间更新。每贴一张海报就是把一个连续的区间的值全部更改,但这里的数据比较大,直接做可能时间空间复杂度都很高。
因为最多只有10000张海报,而即使每张海报的两个端点都不相同,最多就有20000个有用的端点,所以可以采用把这些位置去重,每次二分查找左区间和右区间的位置,然后把左区间和右区间的位置进行线段树的区间更新,查询的时候就用一个vis数组进行标记如果没有用过的标记ans++,用过的直接return。
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <map>
#define ls 2*o
#define rs 2*o+1
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2e4 + 10;
int setv[N<<2];
bool vis[N*2];
struct Item {
int L, R;
} item[N];
int x[N], ans;
void build(int o, int L, int R) {
setv[o] = 0;
if(L == R) return;
int M = (L+R)/2;
build(ls, L, M);
build(rs, M+1, R);
}
void pushdown(int o) {
if(setv[o]) {
setv[ls] = setv[rs] = setv[o];
setv[o] = 0;
}
}
void maintain(int o) {
if(setv[ls] == setv[rs]) setv[o] = setv[ls];
}
int ql, qr, val;
void modify(int o, int L, int R) {
if(ql <= L && R <= qr) {
setv[o] = val;
return ;
}
pushdown(o);
int M = (L+R)/2;
if(ql <= M) modify(ls, L, M);
if(qr > M) modify(rs, M+1, R);
maintain(o);
}
void query(int o, int L, int R) {
if(setv[o]) {
if(!vis[setv[o]]) {
ans++;
vis[setv[o]] = true;
return;
}
else return ;
}
if(L == R) return ;
int M = (L+R)/2;
query(ls, L, M);
query(rs, M+1, R);
}
int main() {
//freopen("in.txt", "r", stdin);
int T;
int n, m;
scanf("%d", &T);
while(T--) {
//init
memset(vis, false, sizeof(vis));
scanf("%d", &m);
n = 0;
for(int i = 0; i < m; i++) {
scanf("%d%d", &item[i].L, &item[i].R);
x[n++] = item[i].L;
x[n++] = item[i].R;
}
//排序去重复并离散化
sort(x, x+n);
n = unique(x, x+n) - x;
//构建线段树
build(1, 1, n);
for(int i = 0; i < m; i++) {
ql = lower_bound(x, x+n, item[i].L) - x+1;
qr = lower_bound(x, x+n, item[i].R) - x+1;
val = i+1;
modify(1, 1, n);
}
ans = 0;
query(1, 1, n);
printf("%d\n", ans);
}
return 0;
}