题目链接http://poj.org/problem?id=2528
思路:
每次贴海报,就把目标区间内的颜色标号标记成当前海报颜色标号,最后查询整个墙的分区间内所以不同颜色的数量就可以
但是因为墙的长度太长了,如果直接用线段树,就会内存超限,所以要进行离散化处理,这里是区间的离散化处理,这里我引用了别的博主的话
引用文章https://blog.csdn.net/sugarbliss/article/details/80501556
原来的:1 2 3 4 6 7 8 10 (去重后排序)
映射后:1 2 3 4 5 6 7 8
原来的:[1, 4] [2, 6] [8, 10] [3, 4] [7, 10]
映射后:[1, 4] [2, 5] [7, 8] [3, 4] [6, 8]
很多代码能够AC但是不正确,比如下面的这组数据:
离散化后前:[1, 10] [1, 3] [6, 10]
离散化后是:[1, 4] [1, 2] [3, 4]
离散化前,3与6之间是有间隙的,但是离散化后,2与3相连的,于是原来3与6之间的部分就看不到,少算了,得到的结果是2,而正解是3。
为了解决这个问题,我们可以对排序后的数组处理一下。比如[1, 3, 6, 10]如果相邻数字间距大于1,就在其中加上任意一个数字,比如加成[1, 3, 4, 6, 7, 10, 11],这样处理就好了。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
const int maxn = 20000 + 100;
int col_lazy[maxn<<3],vis[maxn],c[maxn<<1];
int cnt;
//col_lazy就是记录当前区间颜色,-1为杂色或者没有海报
//vis就是最后求取总区间内出现过多少不同数字时防止重复计算的标记
//这里要注意maxn,给一万个询问,就是会出现两万个数字,a最坏存4万个,lazy一般是a的4倍,vis只要两万
//a是存离散化数字的数组,就是存海报左右区间中出现过的数字
//cnt答案数量
struct node
{
int l,r;
}p[maxn];//存未进行离散化前的坐标
//下推标记数组,-1为杂色或者没有海报
void PushDown(int rt)
{
//如果这个区间只有一种海报颜色,就向下更新
if(col_lazy[rt] != -1){
col_lazy[rt<<1] = col_lazy[rt];
col_lazy[rt<<1|1] = col_lazy[rt];
col_lazy[rt] = -1;
}
}
//更新颜色
void Update(int L,int R,int l,int r,int rt,int C)
{
//把区间内的颜色更新成最新贴的海报颜色
if(L <= l&&r <= R){
col_lazy[rt] = C;
return ;
}
int m = (r + l)>>1;
PushDown(rt);
if(L <= m){
Update(L,R,l,m,rt<<1,C);
}
if(R > m){
Update(L,R,m + 1,r,rt<<1|1,C);
}
}
void Query(int L,int R,int l,int r,int rt)
{
//如果这个区间有杂色或者没有颜色,那么就向下寻找
if(L <= l&&r <= R&&col_lazy[rt] != -1){
if(!vis[ col_lazy[rt] ]){
/*如果当前海报颜色计算过了,就跳过
防止一个海报的区间被分割成多个子区间后重复计算*/
vis[ col_lazy[rt] ] = 1;
cnt++;
}
return ;
}
//说明这块空间没有使用,不写就会死循环
if(l == r){
return ;
}
int m = (l + r)>>1;
//不需要PushDown函数,因为在Update时就已经将所有状态更新完毕
if(L <= m){
Query(L,R,l,m,rt<<1);
}
if(R > m){
Query(L,R,m + 1,r,rt<<1|1);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(vis,0,sizeof(vis));
memset(col_lazy,-1,sizeof(col_lazy));
cnt = 0;
//tot就是通用的存a个数的变量
int tot = 1;
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++){
scanf("%d%d",&p[i].l,&p[i].r);
c[tot++] = p[i].l;
c[tot++] = p[i].r;
}
//离散化
sort(c + 1,c + tot);
tot = unique(c + 1,c + tot) - c;//防止重复数据干扰
int k = tot;
for(int i = 2; i < k; i++){
if(c[i] - c[i - 1] > 1){
//区间离散的补充
c[tot++] = c[i - 1] + 1;
}
}
sort(c + 1,c + tot);
//二分查找所对应的离散化后的坐标
for(int i = 1; i <= n; i++){
int l,r;
l = lower_bound(c + 1,c + tot,p[i].l) - c;
r = lower_bound(c + 1,c + tot,p[i].r) - c;
Update(l,r,1,tot - 1,1,i);
}
Query(1,tot - 1,1,tot - 1,1);
cout<<cnt<<endl;
}
return 0;
}
本文介绍了一种在解决POJ 2528题目的过程中采用的离散化处理方法,针对大规模数据集,通过离散化减少内存消耗,避免线段树内存超限。文章详细阐述了离散化原理,包括原始数据的映射过程,以及如何通过增加中间数值解决区间连续性问题,确保计算准确性。

419

被折叠的 条评论
为什么被折叠?



