poj2528

12 篇文章 0 订阅
6 篇文章 0 订阅

题目不难,线段树+离散化

大致题意为:给定n张宣传画的起点和终点,后张贴的宣传画可能会覆盖先张贴的宣传画,现要求出可以看见多少张宣传画。

典型的线段树题目。分析如下:

1)离散化

由于题目中宣传画的地点和终点在1到1000W之间,如果不离散化则要创建1000W的线段树,空间消耗太大,且时间上也会TLE。所以必须要离散化。离散化的思想也很简答:将所有的输入中的数字排序,然后重新给每个数字编号(从1开始)。这样就实现了离散化,而离散化后的线段树和没有离散化的线段树处理相同,结果也相同。

有多种方式可以实现离散化,这里介绍两种:

11)借助map将数字和其重编号绑定,确定是占用空间大,由于是STL容器,故时间也消耗不好,优点:操作简单

12)采用更加灵活的方式:定义结构体point,由宣传画的起点或终点位置以及在原始输入中的序号组成,若为起点则序号为实际序号+1后取反,若为终点则序号为实际序号+1,从(-1,,+1)开始,然后对结构体数组排序,接着重新编号,在编号的同时也更新了原始输入,使得原始输入为重新编号后的相应输入。

2)线段树相关操作

本题线段树的操作时难点。首先线段树节点的定义如下:起点、终点、权值。其中权值value这里解释一下:

21)若为0,则表示没有宣传画覆盖或者是被若干宣传画覆盖(多余1张)

22)value若大于0,则表示被编号为value的宣传画覆盖。

线段树的创建就不说了。这里主要讲解一下线段树的更新操作和查询操作

线段树的更新操作和查询操作:

线段树的更新和查询操作取决于线段树节点中的权值定义。不同题目权值的定义不同所相对应的更新和查询操作时不同的。这里就从权值的定义出发:若此时有一张宣传画,则对与该画位置相对应的线段树节点赋值value(即正好覆盖或拆分覆盖);否则在遍历寻找节点的同时要对value>0的父节点更新权值(注意先更新其儿子节点的权值,赋值为父亲节点的权值,然后更新父亲节点的权值为0)这个要自己体会,不好说明白!!!

而查询操作在理解更新操作后就很明了,直接查询可以看见的不同宣传画个数即可。

1)map离散化代码

下面是代码:3484K+469MS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>
#define Max 10010
#define Lson(p) (p<<1)
#define Rson(p) (p<<1|1)
using namespace std;
int Input[Max][2]; //原始输入数据
int record[Max*2];//排序辅助数组
bool flag[Max];//标记编号为i的宣传画是否已经存在,依次来计数最后可以看见的宣传画
int c,n,range;
int ans; //最终结果
struct Node{ //线段树节点
	int l,r;
	int value; //权值
}node[Max*8]; //注意这里不要*4,因为实际上离散化后最大的位置大于Max
map<int,int> Cmp; //map容器
void build_tree(int left,int right,int po){  // 建线段树
	node[po].l=left,node[po].r=right,node[po].value=0; //权值初始化为0,即没有画覆盖
	if(left==right)
		return ;
    int mid=(left+right)>>1;
	build_tree(left,mid,Lson(po));
	build_tree(mid+1,right,Rson(po));
}
void update_tree(int left,int right,int v,int po){ //更新线段树
	if(left==node[po].l && right==node[po].r){ //若正好覆盖线段树节点,则赋值权值
		node[po].value=v;
		return ;
	}
	if(node[po].value){ //否则若节点的权值大于0,则要更新权值,注意顺序,先更新儿子节点,后更新父节点
		node[Lson(po)].value=node[po].value;
		node[Rson(po)].value=node[po].value;
		node[po].value=0;
	}
	int mid=(node[po].l+node[po].r)>>1; //继续更新节点
	if(right<=mid)
		update_tree(left,right,v,Lson(po));
	else if(left>=mid+1)
		update_tree(left,right,v,Rson(po));
	else{
		update_tree(left,mid,v,Lson(po));
		update_tree(mid+1,right,v,Rson(po));
	}
}
void search_tree(int left,int right,int po){ // 查询线段树
	if(left==node[po].l && right==node[po].r && node[po].value>0){ // 若已经覆盖
		if(!flag[node[po].value]){ //若该编号的宣传画还没有出现,则可以看见的宣传画个数累计1,并置标记
			flag[node[po].value]=true;
			ans++;
		}
		return ;
	}
	int mid=(left+right)>>1;
	search_tree(left,mid,Lson(po)); // 不断查询”整棵树“
	search_tree(mid+1,right,Rson(po));
}	
int main(){
	scanf("%d",&c); 
	while(c--){
		scanf("%d",&n);
		int index=0;
		for(int i=0;i<n;i++){
			scanf("%d%d",&Input[i][0],&Input[i][1]);
			record[index++]=Input[i][0];
			record[index++]=Input[i][1];
		}
		sort(record,record+index); //排序
		range=0;
		Cmp[record[0]]=++range;
		for(int i=1;i<index;i++) //重新编号
			if(record[i]>record[i-1])
				Cmp[record[i]]=++range;
		build_tree(1,range,1); //建树
		for(int i=0;i<n;i++) //更新树
			update_tree(Cmp[Input[i][0]],Cmp[Input[i][1]],i+1,1);
		ans=0;
		memset(flag,0,sizeof(flag)); //初始化为都没有出现
		search_tree(1,range,1); // 查询树
		printf("%d\n",ans); //输出结果
	}
	return 0;
}

2)数组离散化代码

下面是代码:1176K+79MS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define Max 10010
#define Lson(p) (p<<1)
#define Rson(p) (p<<1|1)
#define Mid(a,b)  ((a+b)>>1)
using namespace std;
int Input[Max][2];
struct Point{ //辅助节点
	int truth; //实际位置
	int index; //实际输入编号
}point[2*Max];
struct Node{ // 线段树节点,定义与上面1)的定义相同
	int l,r;
	int value;
}node[8*Max];
bool flag[Max];
int c,n,ans;
bool cmp(const struct Point p,const struct Point q){ //覆盖cmp
	return p.truth<q.truth;
}
void build_tree(int left,int right,int po){ //建树
	node[po].l=left,node[po].r=right,node[po].value=0;
	if(left==right)
		return ;
	int mid=Mid(left,right);
	build_tree(left,mid,Lson(po));
	build_tree(mid+1,right,Rson(po));
}
void update_tree(int left,int right,int v,int po){ //更新树
	if(left==node[po].l && right==node[po].r){
		node[po].value=v;
		return ;
	}
	if(node[po].value){
		node[Lson(po)].value=node[po].value;
		node[Rson(po)].value=node[po].value;
		node[po].value=0;
		
	}
	int mid=Mid(node[po].l,node[po].r);
	if(right<=mid)
		update_tree(left,right,v,Lson(po));
	else if(left>=mid+1)
		update_tree(left,right,v,Rson(po));
	else{
		update_tree(left,mid,v,Lson(po));
		update_tree(mid+1,right,v,Rson(po));
	}
}
void search_tree(int left,int right,int po){ //查询
	if(left==node[po].l && right==node[po].r && node[po].value>0){
		if(!flag[node[po].value]){
			flag[node[po].value]=true;
			ans++;
		}
		return ;
	}
	int mid=Mid(left,right);
	search_tree(left,mid,Lson(po));
	search_tree(mid+1,right,Rson(po));
}		   
int main(){
	scanf("%d",&c);
	while(c--){
		scanf("%d",&n);
		for(int i=0;i<n;i++){ //离散化核心代码
			scanf("%d%d",&Input[i][0],&Input[i][1]); //注意负序号表示为起点,正序号为终点
			point[2*i].truth=Input[i][0];
			point[2*i].index=-(i+1); // 开始为-1,接着为-2、-3、……
		    point[2*i+1].truth=Input[i][1];
			point[2*i+1].index=i+1; //开始为1,接着为2、3、……
		}
		sort(point,point+2*n,cmp); // 排序
		int temp=point[0].truth;
		int Count=1; 
		for(int i=0;i<2*n;i++){ //重新编号,核心代码
			if(point[i].truth!=temp){ //若不同则增加序号
				Count++;
				temp=point[i].truth;
			}
			if(point[i].index<0) // 处理起点
				Input[-point[i].index-1][0]=Count;
			else //处理终点
				Input[point[i].index-1][1]=Count;
		}
		build_tree(1,Count,1); //建树
		for(int i=0;i<n;i++) //更新树
			update_tree(Input[i][0],Input[i][1],i+1,1);
		memset(flag,0,sizeof(flag));
		ans=0;
		search_tree(1,Count,1); //查询树
		printf("%d\n",ans);
	}
	return 0;
}
		



 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值