POJ_Mayor's Poster_线段树、离散化

每次A题都要听一次小苹果,感觉自己萌萌哒。


题意:

一个线段上10^7个点可以“涂色”,每个点只能涂一种,后涂覆盖先涂,离线进行最多10^5次操作,每次区间涂一种新的颜色,最后求问一共有多少中颜色。(线段上本来无色)。



Input

The first line of input contains a number c giving the number of cases that follow. The first line of data for a single case contains number 1 <= n <= 10000. The subsequent n lines describe the posters in the order in which they were placed. The i-th line among the n lines contains two integer numbers l i and ri which are the number of the wall segment occupied by the left end and the right end of the i-th poster, respectively. We know that for each 1 <= i <= n, 1 <= l i <= ri <= 10000000. After the i-th poster is placed, it entirely covers all wall segments numbered l i, l i+1 ,... , ri.

Output

For each input data set print the number of visible posters after all the posters are placed.


一开始在纠结10^5次涂色怎么更新树,后来看了题解也没提,然后才想起来离线操作只要向下更新就好了,不用回溯,反正最后遍历一遍整棵树就可以。。。。。

然后题解里提了我没想到的问题,线段长达10^7,空间开销很大,10^7个int需要32*10^7 / (8*1024)K,大概4*10^4K,题目给了6*10^4K,线段树肯定会MLE。由于线段树很大,也会脱慢速度,不过O(logn)的算法速度的影响应该还不是很大。

为了解决这个问题,要用离散化操作。离散化是把较大空间的点映射到一个较小的空间里去。具体操作就是先读入所有操作,把每个操作的l和r记录,去重,然后排序,重新编号,这样原来的空间就紧凑很多了。这道题这里有个问题,不能只离散化l和r两个点,要多加他们两端的两个点,否则会过度压缩,具体什么情况POJ的discuss里有,但是我没这么处理也过了,应该是数据水。。。总不能是标程出错了吧。

在离散化的过程中我的办法不是很好,看了下空间开销,我的是4*10^4多,和预估的一样,但是很多代码都是三次方级别的空间,想了想没想到什么好的办法,用map什么的存储pair然后用二分查找,那样常数大一点但是复杂度会降下去。


代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
#define mxn 20100
#define mxl 10000010
int ll[mxn<<2],rr[mxn<<2];
int color[mxn<<2],flag[mxn<<2];
int n,l[mxn],r[mxn];
int mapp[mxl],dis[mxn];
int hash;
void build(int l,int r,int id){
	ll[id]=l,rr[id]=r,color[id]=-1,flag[id]=-1;
	if(l==r)	return;
	int m=(l+r)>>1,ls=id<<1,rs=ls|1;
	build(l,m,ls);
	build(m+1,r,rs);
}
void down(int id);
void update(int l,int r,int x,int id){
	if(ll[id]==l&&rr[id]==r){
		flag[id]=x;
		color[id]=x;
		return;
	}
	color[id]=-1;
	if(flag[id]!=-1)	down(id);
	int m=(ll[id]+rr[id])>>1,ls=id<<1,rs=ls|1;
	if(r<=m)	update(l,r,x,ls);
	else if(l>m)	update(l,r,x,rs);
	else{
		update(l,m,x,ls);
		update(m+1,r,x,rs);
	}
}
void down(int id){
	if(ll[id]==rr[id])	return;
	int m=(ll[id]+rr[id])>>1,ls=id<<1,rs=ls|1;
	update(ll[id],m,flag[id],ls);
	update(m+1,rr[id],flag[id],rs);
	flag[id]=-1;
}
void find(int id){
	if(color[id]!=-1){
		flag[color[id]]=1;
		return;
	}
	if(ll[id]==rr[id])	return;

	int ls=id<<1,rs=ls|1;
	find(ls);
	find(rs);
}
int ans(){
	int ret=0;
	for(int i=0;i<n;++i)	if(flag[i])
		++ret;
	return ret;
}
int main(){
	int cs;
	scanf("%d",&cs);
	while(cs--){
		scanf("%d",&n);
		memset(mapp,0,sizeof(mapp));
		for(int i=0;i<n;++i){
			scanf("%d%d",&l[i],&r[i]);
			dis[i*2]=l[i];//应多加两个点离散化,但是那样POJ过不过就不知道了
			dis[i*2+1]=r[i];
		}
		hash=0;
		sort(dis,dis+2*n);
		for(int i=0;i<2*n;++i)	if(!mapp[dis[i]])
			mapp[dis[i]]=++hash;
		for(int i=0;i<n;++i){
			l[i]=mapp[l[i]];
			r[i]=mapp[r[i]];
		}
		build(1,hash,1);
		for(int i=0;i<n;++i)
			update(l[i],r[i],i,1);
		memset(flag,false,sizeof(flag));
		find(1);
		printf("%d\n",ans());
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值