题意:在一个墙上贴各种海报,每种海报给定贴的位置,问最后全部贴完时,能看到几种海报(看到一点也算)。
思路:涂颜色问题。数据很大,不能用朴素算法,线段树+离散化(非一般离散化),树的每个节点维护一个区间,一个海报标记(0:无有海报 -1:存在多种海报 >0:具体的海报种类。(海报种类从1-n)。对于普通的离散化,在一些特殊数据下会存在丢失某些区间。比如[1 10] [1 3] [6 10]正确结果是3,但是一般的离散化结果是2。因为在建树的时候。1 3 6 10->映射1 2 3 4 建(1,5)的树,那么根为(1 5)==>[1 10] 左子树为(1,3)==>[1 3] 右子树为(3 5)==>[6 10] 那么区间[4 5]就会缺失,从而影响了对后面的操作,那么如何解决这个问题呢? 很简单,如果两个相邻的区间(左区间的右顶点<右区间的左顶点),那么就在两个区间中间插入一个数(数>
左区间的右顶点&&数<右区间的左顶点)。比如 [1 3] [6 10] (6>3),那么就在3 6直接插入一个数(4/5)。这样就能避免区间丢失的情况了。
代码:
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
#define INF 0x3f3f3f3f
#define L(k)(k<<1)
#define R(k)(k<<1|1)
const int MAXN = 10005;
struct point
{
int l, r;
};
point pos[MAXN],temp[MAXN];
//区间,区间副本
struct Node//数结点
{
int l, r;
int col;
};
Node tree[MAXN*7];//树
int sortpos[MAXN*3],vis[MAXN],total;
//离散化数组,海报类型数组,统计能看到的海报数目
bool cmp(point a, point b)//比较函数
{
return a.r < b.l;
}
void build(int k, int start, int end)
{
tree[k].l = start;
tree[k].r = end;
tree[k].col = 0;//0表示无海报覆盖
if (start + 1 != end)
{
int mid = (start + end) >> 1;
build(L(k), start, mid);
build(R(k), mid, end);
}
}
void init(int k, int start, int end,int c)
{
if (start>=tree[k].r||end<=tree[k].l)
{//区间无交集
return;
}
if (start <= tree[k].l&&end >= tree[k].r)
{//完全包含区间
tree[k].col = c;
return;
}
//否则海报可能覆盖区间的某些部分
if (tree[k].col >= 0)
{//先让孩子结点继承父亲结点的海报
tree[L(k)].col = tree[k].col;
tree[R(k)].col = tree[k].col;
tree[k].col = -1;//多种海报覆盖
}
init(L(k), start,end, c);//递归左孩子
init(R(k), start,end, c);//递归右孩子
}
void dfs(int k)
{
if (tree[k].col == -1)//此区间又多种海报覆盖
{
dfs(L(k));
dfs(R(k));
}
else//只要一种或者没有海报
{
if ((!vis[tree[k].col]) && tree[k].col>0)
{//col=0表示区间没有海报覆盖,vis表示此种海报是否已经统计过
total++;//统计+1
vis[tree[k].col] = 1;//标记出现过
}
}
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
total = 0;
int n,ans=0;
scanf("%d", &n);
memset(vis, 0, sizeof(vis));
for (int i = 0; i < n; i++)
{
scanf("%d %d", &pos[i].l, &pos[i].r);
temp[i].l = pos[i].l;
temp[i].r = pos[i].r;
sortpos[ans++] = pos[i].l;
sortpos[ans++] = pos[i].r;
}
sort(temp, temp + n, cmp);
//用来判断两个相邻区间之间前面区间的结尾是否大于后面区间的开始
//如果是的话,那么加上一个他们之间的数(不能等于),防止离散化后
//出现一些区间消失了的情况
for (int i = 1; i < n; i++)
{
if (temp[i].l - temp[i - 1].r > 1)
{
sortpos[ans++] = temp[i - 1].r + 1;
}
}
sort(sortpos, sortpos + ans);//离散化
//int q = unique(sortpos, sortpos + ans)-sortpos;
build(1, 1, ans+1);//建树
for (int i = 0; i < n; i++)
{//每个"点"为长度=1的区间,比如点1为[1,2)
int start = (lower_bound(sortpos , sortpos + ans , pos[i].l) - sortpos) + 1;
//二分回来时从0开始的。+1:下标从1开始,+2:因为每个"点"是长度=1的区间
int end = (lower_bound(sortpos , sortpos + ans , pos[i].r) - sortpos) + 2;
init(1, start, end, i + 1);//下标从1开始到ans+1
}
dfs(1);//找能看到的海报数目
printf("%d\n", total);
}
return 0;
}
不知道为什么去重之后就WA,不去重就AC。想不明白。