P1382 楼房
题目描述:
地平线(x轴)上有n个矩(lou)形(fang),用三个整数h[i],l[i],r[i]来表示第i个矩形:矩形左下角为(l[i],0),右上角为(r[i],h[i])。地平线高度为0。在轮廓线长度最小的前提下,从左到右输出轮廓线。
下图为样例2。
输入格式:
第一行一个整数n,表示矩形个数
以下n行,每行3个整数h[i],l[i],r[i]表示第i个矩形。
输出格式:
第一行一个整数m,表示节点个数
以下m行,每行一个坐标表示轮廓线上的节点。从左到右遍历轮廓线并顺序输出节点。第一个和最后一个节点的y坐标必然为0。
初看这题,并不知道和线段树是什么关系;在看了题解以后,才知道可以用线段树维护每个点的最大高度,就是整个图形的轮廓线;但是细节真的很多。首先,我们如果用区间数组存x坐标来做,x的范围是10^-9 到10^9,肯定爆内存,所以第一步就是离散化x坐标,具体怎么离散化看代码自己模拟一下就知道了;离散化完了以后,我们就要区间修改最大值了;这道题的最关键之处在于,我们要知道一个轮廓线肯定有一个横线,那么这个横线的两头的y坐标都相同,那么我们知道了一头的y坐标,另一头也就知道了,并且如果两个y坐标不相等,那么轮廓线肯定发生变化;那么我们求区间最大值时,只要更新到右端点-1就行,最后输出按竖线的方式输出;
代码:
#include<bits/stdc++.h>
using namespace std;
int h[200000],L[200000],R[200000],a[400000],b[400000];
int f1[200000],f2[200000];
int x,y,z;
struct Node{
int l,r,w,f;
}tree[800000];
inline void build(int k,int ll,int rr){
tree[k].l=ll,tree[k].r=rr,tree[k].f=0;
if(ll==rr){
tree[k].w=0;
return;
}
int m=(ll+rr)>>1;
build(k<<1,ll,m);
build(k<<1|1,m+1,rr);
}
inline void pd(int k){
if(tree[k].f){
tree[k<<1].f=max(tree[k].f,tree[k<<1].f);
tree[k<<1|1].f=max(tree[k].f,tree[k<<1|1].f);
tree[k<<1].w=max(tree[k<<1].w,tree[k].f);
tree[k<<1|1].w=max(tree[k<<1|1].w,tree[k].f);
tree[k].f=0;
}
}
inline void change(int k){
if(tree[k].l>=x&&tree[k].r<=y){
tree[k].w=max(tree[k].w,z);
tree[k].f=max(tree[k].f,z);
return;
}
pd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) change(k<<1);
if(y>m) change(k<<1|1);
tree[k].w=max(tree[k<<1].w,tree[k<<1|1].w);
}
inline int ask(int k){
if(tree[k].l==tree[k].r){
return tree[k].w;
}
pd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) ask(k<<1);
else ask(k<<1|1);
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&h[i],&L[i],&R[i]);
a[i*2-1]=L[i],a[i*2]=R[i];//第一步离散化,手动模拟一下就知道了
}
sort(a+1,a+2*n+1);
b[1]=a[1];
int ans=1;//这个是1,不是0
for(int i=2;i<=2*n;i++){//第二步离散化,相当于把a数组去重
if(a[i]!=a[i-1]) b[++ans]=a[i];
}
build(1,1,ans);
for(int i=1;i<=n;i++){
x=lower_bound(b+1,b+ans+1,L[i])-b;
y=lower_bound(b+1,b+ans+1,R[i])-b-1;//只需更新右边界ans-1就行,ans不用更新
//cout<<x<<" "<<y<<endl;
z=h[i];
change(1);//更新区间最大y坐标值
}
int sum=0;//所有横线右端点的数量,非常重要
for(int i=1;i<=ans;i++){
f1[i]=b[i];//表示一个点的x坐标
x=i;
f2[i]=ask(1);//表示一个点的最高y坐标
//cout<<f2[i]<<endl;
if(f2[i]!=f2[i-1]) sum++;//仔细想想,一个横线的右端点只要一个,并且不相等
}
printf("%d\n",sum*2);//输出总端点。sum*2
for(int i=1;i<=ans;i++){
if(f2[i]!=f2[i-1]){//跟上面一个原理
printf("%d %d\n",f1[i],f2[i-1]);
printf("%d %d\n",f1[i],f2[i]);
}
}
return 0;
}
更新一个扫描线做法,基本思路和模板一样,但是要特别注意,模板在有重边时求面积是没有任何问题的,但是这个轮廓线却有问题;
当入边有重边时,只要计算 Y 2 Y_2 Y2最高的点就行,所以排序特别关键,一定要保证 Y 2 Y_2 Y2排在前面;当出边有重边时,保证 Y 2 Y_2 Y2高的排在后面,跟入边相反;
代码:
#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=100100;
const int M=50100;
const LL mod=10007;
int n,h[N],l[N],r[N],a[N],cnt,tot,val[N];
vector<pa>ans,ve;
struct D{
int Y1,Y2,X,f;
}du[N*2];
bool cmp(D p,D q){
if(p.X==q.X){
if(p.f==q.f){
if(p.f==1) return p.Y2>q.Y2;
else if(p.f==-1) return p.Y2<q.Y2;
}
return p.f>q.f;
}
return p.X<q.X;
}
struct Node{
int l,r,lz,len;
}tr[N*8];
void build(int l,int r,int k){
tr[k].l=l,tr[k].r=r;
if(l==r) return;
int d=(l+r)>>1;
build(l,d,ls);
build(d+1,r,rs);
}
void pp(int k){
if(tr[k].lz) tr[k].len=val[tr[k].r+1]-val[tr[k].l];
else if(tr[k].l==tr[k].r) tr[k].len=0;
else tr[k].len=tr[ls].len+tr[rs].len;
}
void update(int l,int r,int w,int k){
if(tr[k].l>=l&&tr[k].r<=r){
tr[k].lz+=w;
pp(k);
return;
}
int d=(tr[k].l+tr[k].r)>>1;
if(l<=d) update(l,r,w,ls);
if(r>d) update(l,r,w,rs);
pp(k);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d%d",&h[i],&l[i],&r[i]),a[i]=h[i];
sort(a+1,a+n+1);
cnt=unique(a+1,a+n+1)-a-1;
for(int i=1;i<=n;i++){
int p=lower_bound(a+1,a+cnt+1,h[i])-a;
val[p]=h[i];
du[++tot].f=1,du[tot].X=l[i],du[tot].Y1=0,du[tot].Y2=p;
du[++tot].f=-1,du[tot].X=r[i],du[tot].Y1=0,du[tot].Y2=p;
}
sort(du+1,du+tot+1,cmp);
build(0,cnt-1,1);
for(int i=1;i<=tot;i++){
int mx=tr[1].len;
update(du[i].Y1,du[i].Y2-1,du[i].f,1);
if(du[i].f==1){//入边
if(val[du[i].Y2]>mx){
ans.push_back(pa(du[i].X,mx));
ans.push_back(pa(du[i].X,val[du[i].Y2]));
}
}
else{//出边
if(val[du[i].Y2]>=mx&&val[du[i].Y2]!=tr[1].len){
ans.push_back(pa(du[i].X,val[du[i].Y2]));
ans.push_back(pa(du[i].X,tr[1].len));
}
}
}
ve=ans;
printf("%d\n",(int)ve.size());
for(int i=0;i<ve.size();i++) printf("%d %d\n",ve[i].first,ve[i].second);
return 0;
}