hdu1828 线段树+扫描线计算矩形周长并

hdu1828 Picture
传送门

题意

平面上有\(n(0\leq n < 5000)\)个矩形,每个矩形左下角坐标为\((x_1,y_1)\),右上角坐标为\((x_2,y_2)\),坐标的值是位于\([-10000,10000]\)之中内的整数,计算矩形周长并。

题解

方法一

两次扫描线
使用计算矩形面积并中,线段树+扫描线的方法,离散化一维坐标,从下向上,从左向右做两次扫描线,分别计算横边和竖边的总长度
当前增加的长度等于当前的总长度与上一次的总长度之差的绝对值
ps. G++返回WA,C++能过

#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
#define PLI pair<LL,int>
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define lowbit(x) (x&(-x))
using namespace std;

const int maxn=5010;
int n,m1,m2,k1,k2,X[2*maxn],Y[2*maxn];

struct node{
    int l,r,h,state;
    node(){}
    node(int l,int r,int h,int state):l(l),r(r),h(h),state(state){}
    bool operator < (const node& t) const{
        return h<t.h;
    }
}nodes1[2*maxn],nodes2[2*maxn];

struct SGT{
    int cnt,sum;
}sgt[8*maxn];

int binary_search(int x,int num,int* a){
    int l=1,r=num;
    while(r>=l){
        int mid=(l+r)>>1;
        if(a[mid]==x) return mid;
        if(a[mid]>x) r=mid-1;
        else l=mid+1;
    }
    return -1;
}

void pushup(int o,int l,int r,int* a){
    if(sgt[o].cnt) sgt[o].sum=a[r+1]-a[l];
    else if(l==r) sgt[o].sum=0;
    else sgt[o].sum=sgt[o<<1].sum+sgt[o<<1|1].sum;
}

void build(int o,int l,int r){
    sgt[o].cnt=sgt[o].sum=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
}

void update(int o,int l,int r,int ql,int qr,int v,int* a){
    if(ql<=l && r<=qr){
        sgt[o].cnt+=v;
        pushup(o,l,r,a);
        return;
    }
    int mid=(l+r)>>1;
    if(ql<=mid) update(lson,ql,qr,v,a);
    if(qr>mid) update(rson,ql,qr,v,a);
    pushup(o,l,r,a);
}

int main(){
    while(scanf("%d",&n)!=EOF){
        m1=m2=0;
        for(int i=1;i<=n;i++){
            int x11,y11,x22,y22;
            scanf("%d %d %d %d",&x11,&y11,&x22,&y22);
            ++m1;
            nodes1[m1]=node(x11,x22,y11,1);
            X[m1]=x11;
            ++m1;
            nodes1[m1]=node(x11,x22,y22,-1);
            X[m1]=x22;
            ++m2;
            nodes2[m2]=node(y11,y22,x11,1);
            Y[m2]=y11;
            ++m2;
            nodes2[m2]=node(y11,y22,x22,-1);
            Y[m2]=y22;
        }
        sort(nodes1+1,nodes1+1+m1);
        sort(nodes2+1,nodes2+1+m2);
        sort(X+1,X+1+m1);
        sort(Y+1,Y+1+m2);
        k1=k2=1;
        for(int i=2;i<=m1;i++){
            if(X[i]!=X[i-1]) X[++k1]=X[i];
        }
        for(int i=2;i<=m2;i++){
            if(Y[i]!=Y[i-1]) Y[++k2]=Y[i];
        }
        int ans=0,last=0;
        build(1,1,k1-1);
        for(int i=1;i<=m1;i++){
            int l=binary_search(nodes1[i].l,k1,X);
            int r=binary_search(nodes1[i].r,k1,X)-1;
            update(1,1,k1-1,l,r,nodes1[i].state,X);
            ans+=abs(sgt[1].sum-last);
            last=sgt[1].sum;
        }
        last=0;
        build(1,1,k2-1);
        for(int i=1;i<=m2;i++){
            int l=binary_search(nodes2[i].l,k2,Y);
            int r=binary_search(nodes2[i].r,k2,Y)-1;
            update(1,1,k2-1,l,r,nodes2[i].state,Y);
            ans+=abs(sgt[1].sum-last);
            last=sgt[1].sum;
        }
        printf("%d\n",ans);
    }
}

方法二

一次扫描线
从下向上扫描,横边长度按照两次扫描线的方法计算,同时统计竖边长度

线段树节点中增加三个变量:
num:当前区间内的竖边总数
ls:当前区间的左端点是否被覆盖
rs:当前区间的右端点是否被覆盖

更新方法为:
如果当前区间被完全覆盖,则\(num=2,ls=rs=true\)
否则如果当前为叶节点,则进行初始化:\(num=0,ls=rs=false\)
否则,\(num\)为左儿子和右儿子的\(num\)之和,\(ls\)与左儿子的\(ls\)一致,\(rs\)与右儿子的\(rs\)一致。如果左儿子的\(rs\)和右儿子的\(ls\)均为\(true\),则当前节点\(num-=2\)

当前竖边总长度的计算方法为当前所有竖边的数量(线段树根节点的\(num\)值)乘上当前扫描线和后一条扫描线的高度差
ps. G++,C++都能过

#include<bits/stdc++.h>
#define LL long long
#define PII pair<int,int>
#define PLI pair<LL,int>
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define lowbit(x) (x&(-x))
using namespace std;

const int maxn=5010;
int n,m,k,X[2*maxn];

struct node{
    int l,r,h,state;
    node(){}
    node(int l,int r,int h,int state):l(l),r(r),h(h),state(state){}
    bool operator < (const node& t)const{
        if(h==t.h) return state>t.state;
        return h<t.h;
    }
}nodes[2*maxn];

struct SGT{
    int cnt,sum,num;
    bool ls,rs;
}sgt[8*maxn];

int binary_search(int x){
    int l=1,r=k;
    while(r>=l){
        int mid=(l+r)>>1;
        if(X[mid]==x) return mid;
        if(X[mid]>x) r=mid-1;
        else l=mid+1;
    }
    return -1;
}

void build(int o,int l,int r){
    sgt[o].cnt=sgt[o].sum=sgt[o].num=0;
    sgt[o].ls=sgt[o].rs=false;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
}

void pushup(int o,int l,int r){
    if(sgt[o].cnt){
        sgt[o].sum=X[r+1]-X[l];
        sgt[o].num=2;
        sgt[o].ls=sgt[o].rs=true;
    }
    else if(l==r){
        sgt[o].sum=sgt[o].num=0;
        sgt[o].ls=sgt[o].rs=false;
    }
    else{
        sgt[o].sum=sgt[o<<1].sum+sgt[o<<1|1].sum;
        sgt[o].num=sgt[o<<1].num+sgt[o<<1|1].num;
        sgt[o].ls=sgt[o<<1].ls;
        sgt[o].rs=sgt[o<<1|1].rs;
        if(sgt[o<<1].rs && sgt[o<<1|1].ls) sgt[o].num-=2;
    }
}

void update(int o,int l,int r,int ql,int qr,int state){
    if(ql<=l && r<=qr){
        sgt[o].cnt+=state;
        pushup(o,l,r);
        return;
    }
    int mid=(l+r)>>1;
    if(ql<=mid) update(lson,ql,qr,state);
    if(qr>mid) update(rson,ql,qr,state);
    pushup(o,l,r);
}

int main(){
    while(scanf("%d",&n)!=EOF){
	    m=0;
	    for(int i=1;i<=n;i++){
	        int x11,y11,x22,y22;
	        scanf("%d %d %d %d",&x11,&y11,&x22,&y22);
	        ++m;
	        nodes[m]=node(x11,x22,y11,1);
	        X[m]=x11;
	        ++m;
	        nodes[m]=node(x11,x22,y22,-1);
	        X[m]=x22;
	    }
	    sort(nodes+1,nodes+1+m);
	    sort(X+1,X+1+m);
	    k=1;
	    for(int i=2;i<=m;i++){
	        if(X[i]!=X[i-1]) X[++k]=X[i];
	    }
	    build(1,1,k-1);
	    int ans=0,last=0;
	    for(int i=1;i<=m;i++){
	        int l=binary_search(nodes[i].l);
	        int r=binary_search(nodes[i].r)-1;
	        update(1,1,k-1,l,r,nodes[i].state);
	        ans+=abs(sgt[1].sum-last);
	        ans+=sgt[1].num*(nodes[i+1].h-nodes[i].h);
	        last=sgt[1].sum;
	    }
	    printf("%d\n",ans);
    }
}
posted @ 2020-10-08 22:29  fxq1304  阅读(8)  评论(0编辑  收藏  举报