题意:
市长竞选,每个市长都往墙上贴海报,海报之间彼此可以覆盖,给出粘贴顺序和每个海报的起点和长度,问最后有多少海报是可见的。
代码:
不是线段树新手应该能看懂,或者说做这题还是wa的同学,下面注释写的都是细节;
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define MAXN 10008
using namespace std;
struct Node{
int l, r, v;
} lines[16*MAXN];
struct item {
int coord, id;
} poster[4*MAXN];
int n, T, li[MAXN], ri[MAXN], rini[MAXN*2];
int res[MAXN];
void build(int k, int l, int r) {//建树
lines[k].l = l;
lines[k].r = r;
lines[k].v = 0;
if (l == r) return ;
int mid = (l+r)/2;
build(k*2, l, mid);
build(k*2+1, mid+1, r);
return ;
}
void query(int k, int l, int r, int i) {//节点更新
if (lines[k].l == l && lines[k].r == r) {
lines[k].v = i;
return ;
}
//若节点k已经被覆盖, 需先将k的子节点改成k的颜色,然后本身赋0;
if (lines[k].v > 0) {
lines[k*2].v = lines[k].v;
lines[k*2+1].v = lines[k].v;
lines[k].v = 0;
}
int mid = (lines[k].l+lines[k].r)/2;
if (l > mid)
query(k*2+1, l, r, i);
else if (r <= mid)
query(k*2, l, r, i);
else {
query(k*2, l, mid, i);
query(k*2+1, mid+1, r, i);
}
}
int dblup(int l, int r, int v) { //二分查找ID
while (l <= r) {
int mid = (l+r)/2;
if (poster[mid].coord == v)
return poster[mid].id;
if (poster[mid].coord > v)
r = mid-1;
else l = mid+1;
}
return 0;
}
int cmp(item aa, item bb) {
return aa.coord < bb.coord;
}
void getres(int k, int res[]) {
if (lines[k].v > 0) {
res[lines[k].v] = 1;
return ;
}
if (lines[k].l == lines.r) return ;//并不是1~j-1都被覆盖了,少了会re;
getres(2*k, res);
getres(2*k+1, res);
}
int main() {
//freopen("in.txt", "r", stdin);
scanf("%d", &T);
while (T--) {
memset(res, 0, sizeof(res));
scanf("%d", &n);
int j = 1;
for(int i = 1; i <= n; i++) {
scanf("%d%d", li+i, ri+i);
rini[j++] = li[i];//把每一幅海报都记录下来,以便离散化;
rini[j++] = ri[i];
}
rini[j] = -1;
sort(rini+1, rini+j);
j = 1;
for(int i = 1; i <= 2*n; i++,j++) {
if (rini[i] - rini[i-1] > 1 && i != 1) {//距离大于一的,要加一个节点(不好解释);
poster[j].coord = rini[i]-1;
poster[j].id = j;
j++;
}
poster[j].coord = rini[i];
poster[j].id = j;
while (rini[i+1] == rini[i]) i++;//删除重复节点
}
build(1, 1, j-1);
for(int i = 1; i <= n; i++) {
int aa = dblup(1, j-1, li[i]);
int bb = dblup(1, j-1, ri[i]);
query(1, aa, bb, i);
}
getres(1, res);
int result = 0;
for(int i = 1; i <= n; i++)
if (res[i]) {
result++;
}
printf("%d\n", result);
}
return 0;
}
贴一个精简版, 就是把结构体改成了数组,减少了很多内存,时间复杂度并没有变;
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define MAXN 10008
using namespace std;
#define lson k<<1, l, mid
#define rson k<<1|1, mid+1, r
struct item {
int coord, id;
} poster[4*MAXN];
int n, T, li[MAXN], ri[MAXN], rini[MAXN*2], res[MAXN], lines[16*MAXN];
void query(int li, int ri, int i, int k, int l, int r) {
if ( li <= l && ri >= r) {
lines[k] = i;
return ;
}
if (lines[k] > 0) {
lines[k*2] = lines[k*2+1] = lines[k];
lines[k] = 0;
}
int mid = (l+r)/2;
if (li <= mid) query(li, ri, i, lson);
if (ri > mid) query(li, ri, i, rson);
}
int dblup(int l, int r, int v) {
while (l <= r) {
int mid = (l+r)/2;
if (poster[mid].coord == v)
return poster[mid].id;
if (poster[mid].coord > v)
r = mid-1;
else l = mid+1;
}
return 0;
}
int cmp(item aa, item bb) {
return aa.coord < bb.coord;
}
void getres(int k, int l, int r, int res[]) {
if (lines[k] > 0) {
res[lines[k]] = 1;
return ;
}
if (l == r) return ;
int mid = (l+r)/2;
getres(lson, res);
getres(rson, res);
}
int main() {
//freopen("in.txt", "r", stdin);
scanf("%d", &T);
while (T--) {
memset(res, 0, sizeof(res));
memset(lines, 0, sizeof(lines));
scanf("%d", &n);
int j = 1;
for(int i = 1; i <= n; i++) {
scanf("%d%d", li+i, ri+i);
rini[j++] = li[i];
rini[j++] = ri[i];
}
rini[j] = -1;
sort(rini+1, rini+j);
j = 1;
for(int i = 1; i <= 2*n; i++,j++) {
if (rini[i] - rini[i-1] > 1 && i != 1) {
poster[j].coord = rini[i]-1;
poster[j].id = j;
j++;
}
poster[j].coord = rini[i];
poster[j].id = j;
while (rini[i+1] == rini[i]) i++;
}
for(int i = 1; i <= n; i++) {
int aa = dblup(1, j-1, li[i]);
int bb = dblup(1, j-1, ri[i]);
query(aa, bb, i, 1, 1, j-1);
}
getres(1, 1, j-1, res);
int result = 0;
for(int i = 1; i <= n; i++)
if (res[i]) {
result++;
}
printf("%d\n", result);
}
return 0;
}