poj1177[IOI1998]Picture (扫描线+离散化+线段树,求矩形周长并)

这人讲的很清楚了。我就再说一些细节。做了离散化之后时间和空间确实很优秀,但其实不做离散化也可以过。 扫描线这个算法,就是从左到右拿一根平行于y轴的线去扫描,遇到线段后进行处理,入边和出边。统计答案要注意。线段树负责维护的其实是一个线段覆盖的问题,我的每个节点表示的是一条线段,即a[l]a[r+1],所以我建树只建到m-1即可。复杂度应该是 O(nlogn)

#include
#include
#include
#include
#define N 5005
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,m=0,num=0,ans=0,a[N<<1];
struct lines{
    int x,y1,y2;
    bool f;//true--add;false--delete
}line[N<<1];
struct node{
    bool fl,fr;//左右端点是否覆盖
    int cover;//完全覆盖次数
    int lines;//线段个数 
    int clength;//覆盖长度
}tree[N<<2];
void build(int p,int l,int r){
    tree[p].fl=tree[p].fr=tree[p].cover=tree[p].clength=tree[p].lines=0;
    if(l==r) return;//叶子节点l表示线段[a[l],a[l+1]]
    int mid=l+r>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
inline bool cmp(lines A,lines B){
    if(A.x==B.x) return A.f>B.f;//如果重叠,先入边
    return A.xinline void update(int p){
    if(tree[p].cover) return;
    tree[p].clength=tree[p<<1].clength+tree[p<<1|1].clength;
    tree[p].fl=tree[p<<1].fl;tree[p].fr=tree[p<<1|1].fr;
    tree[p].lines=tree[p<<1].lines+tree[p<<1|1].lines;
    if(tree[p<<1].fr&&tree[p<<1|1].fl) tree[p].lines--;//左右端点连在一起,少一条 
}
void cover(int p,int l,int r,int x,int y){
    if(x<=l&&r<=y){
        if(!tree[p].cover){//如果没完全覆盖 
            tree[p].fl=tree[p].fr=true;
            tree[p].lines=1;tree[p].clength=a[r+1]-a[l];
        }
        tree[p].cover++;return;
    }
    int mid=l+r>>1;
    if(x<=mid) cover(p<<1,l,mid,x,y);
    if(y>mid) cover(p<<1|1,mid+1,r,x,y);
    update(p);
}
void uncover(int p,int l,int r,int x,int y){
    if(x<=l&&r<=y){
        tree[p].cover--;
        if(!tree[p].cover){//减没了,要更新 
            if(l==r){//叶子节点,直接更新 
                tree[p].fl=tree[p].fr=false;
                tree[p].clength=tree[p].lines=0;
            }
            else update(p);
        }
        return;
    }
    int mid=l+r>>1;
    if(x<=mid) uncover(p<<1,l,mid,x,y);
    if(y>mid) uncover(p<<1|1,mid+1,r,x,y);
    update(p);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();
    while(n--){
        int x1=read(),y1=read(),x2=read(),y2=read();
        a[++m]=y1;a[++m]=y2;
        line[++num].x=x1;line[num].y1=y1;
        line[num].y2=y2;line[num].f=true;
        line[++num].x=x2;line[num].y1=y1;
        line[num].y2=y2;line[num].f=false;
    }
    sort(a+1,a+m+1);
    m=unique(a+1,a+m+1)-a-1;//离散化y值 
    build(1,1,m-1);
    sort(line+1,line+num+1,cmp);
    line[0]=line[1];//避免第一次多加 
    int prelines=0;//上一次线段数
    int prelength=0;//上次长度
    for(int i=1;i<=num;++i){
        if(line[i].f) cover(1,1,m-1,lower_bound(a+1,a+m+1,line[i].y1)-a,lower_bound(a+1,a+m+1,line[i].y2)-a-1);
        else uncover(1,1,m-1,lower_bound(a+1,a+m+1,line[i].y1)-a,lower_bound(a+1,a+m+1,line[i].y2)-a-1);
        ans+=prelines*2*(line[i].x-line[i-1].x);//先更新水平的长度 
        prelines=tree[1].lines;
        //更新竖直的高度
        ans+=abs(tree[1].clength-prelength);
        prelength=tree[1].clength; 
    }
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(离散化,其他oj,线段树,扫描线)