题目不难,线段树+离散化
大致题意为:给定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;
}