GDUT_专题五:G - Mayor‘s posters 【线段树,离散化】

题目传送门

题意:

一共有n张海报,可以贴在长度为10000000 的墙上,按顺序给出张贴的海报的区间,后贴的海报可以覆盖已经贴上的海报,问最后能看见的海报的数目。

思路分析:

以海报张贴的顺序给海报标记,然后每张贴一张海报就用线段树标记这个区间记录下是第几张海报,如果之前已经有海报被贴上了,就更新这个区间为新的海报,算是比较直接的想法。然后对于题目所给的墙的长度虽然很大,但是海报的张数最多只有1000张,所以我们需要对题目给出的区间的值进行离散化压缩空间。之后对于区间的维护,将区间换新一种海报后并不会对父节点产生什么影响,所以就不需要考虑up了。只需要在每次对已经覆盖的区间down就行了。

离散化:

这里简单介绍一下离散化(虽然也是为了写这题刚学就是了)

离散化,就是当我们只关心数据的大小关系时,用排名代替原数据进行处理的一种预处理方法。离散化本质上是一种哈希,它在保持原序列大小关系的前提下把其映射成正整数。当原数据很大或含有负数、小数时,难以表示为数组下标,一些算法和数据结构(如BIT)无法运作,这时我们就可以考虑将其离散化。

简单来说就是排序加二分后构造出的一个特殊的桶,虽然存的不是与下标相应的值,但是可以存放这个数在原序列中的相对大小,利用二分查找只需要log级别的时间复杂度。

举个栗子:

对于原区间a:1 2 4 8 10

离散化后我们可以得到b: 1 2 3 4 5

其中b[1]=1,b[2]=2,b[3]=4,b[4]=8,b[5]=10;

这样我们就可以将原来需要维护[1,10]的区间变为只需要维护[1,5]的区间了。 

利用STL大法的话就能很容易的实现了。

AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<math.h>
#include<vector>
#include<queue>
#define INF 0x3f3f3f
#define ll long long
#define fre() freopen(".in", "r", stdin);freopen(".out", "w", stdout);
using namespace std;
#define lnode node<<1
#define rnode node<<1|1
const double eps = 1e-6;
const int N = 1e4 + 10;
const int mod = 1e9 + 7;
int n;
struct tree {
	int l, r,col;
	int mid(){
		return l+r>>1;
	}
} t[N << 4];//开大点总没错(bushi) 
struct hb{
	int l,r;
}s[N];//记录每个海报的区间 
int p[N<<1];//离散化 
bool v[N]; //记录颜色 

void down(int node){//下传数据,如果当前区间有贴海报,就传给子区间 
		t[lnode].col=t[rnode].col=t[node].col;
		t[node].col=0;
}
void build(int node, int l, int r) {//正常建树 
	t[node].l=l;t[node].r=r;t[node].col=0;
	if (l == r) return ;//因为没什么需要初始化的,直接return了 
	int mid = (l + r) >> 1;
	build(node << 1, l, mid);
	build(node << 1 | 1, mid + 1, r);
}

void change(int node, int x, int y, int k) {//修改区间为后面贴的海报 
	if (x <= t[node].l && y >= t[node].r  ) {
		t[node].col=k;
		return ;
	}
	if(t[node].col)	down(node);//如果当前区间有贴海报才下传否则没必要 
	int mid = (t[node].r + t[node].l) >> 1;
	if (x <= mid)change(node << 1, x, y, k);
	if(y>mid) change(node << 1 | 1, x, y, k);
}

void ask(int node,int l ,int r) {//查询,遍历一次线段树,把存在的颜色都记录下来 
	if(t[node].l==t[node].r && t[node].col==0)return ;
	if (t[node].col ) {
		 v[t[node].col]=1;
		return ;
	}
	int mid = (t[node].r + t[node].l) >> 1;
	ask(node << 1, t[node].l, mid);
	ask(node << 1 | 1, mid+1, t[node].r);
}
int main() {
	int T,ans=0;
	cin >> T;
	while (T--){
		ans=0;
		int cnt=0;memset(v,0,sizeof(v));
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			int l,r;
			scanf("%d%d",&l,&r);
			p[++cnt]=l;p[++cnt]=r;//记录每个区间长度 
			s[i].l=l;s[i].r=r;
		}
		sort(p+1,p+cnt+1);//排序 
		int len=unique(p+1,p+cnt+1)-p-1;//离散化  STL大法 
//		for(int i=1;i<=len;i++)printf("p[%d]=%d ",i,p[i]);cout<<endl;//调试 离散化后的数组 
		build(1,1,len);//建树 
		for(int i=1;i<=n;i++){
			s[i].l=lower_bound(p+1,p+len+1,s[i].l)-p;
			s[i].r=lower_bound(p+1,p+len+1,s[i].r)-p;
			//转换成离散化后的值进行区间修改 
			change(1,s[i].l,s[i].r,i);
		}
//		for(int i=1;i<=len<<1;i++)printf("t[%d] [%d,%d] col=%d \n",i,t[i].l,t[i].r,t[i].col);
		ask(1,1,len);//遍历 
		for(int i=1;i<=n;i++){//记录颜色总数 
			if(v[i])ans++;
		}
		cout<<ans<<endl;//输出 
	}
		return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值