hdu 1828、poj1177求矩形周长并 线段树 扫描线

hdu 1828 http://acm.hdu.edu.cn/showproblem.php?pid=1828

poj 1177 http://poj.org/problem?id=1177

 

 

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn= 5010;
struct node{
    int lef, rig, mid, cov;
}seg[8*maxn];
struct point{
    int lef, rig, pos, flag;    //flag为1, 下边, flag为-1 上边
}px[2*maxn], py[2*maxn];     // px 为对y做线段树, py为对x做线段树
int disx[2*maxn], disy[2*maxn];     //离散坐标, 下标为离散值
int n, lenx, leny;            //横纵坐标离散后长度
__int64 ans;
void init(){
    int i, j, k, x1, y1, x2, y2;
    for( i=0; i<n; i++){
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        px[i*2+1].lef= px[i*2].lef= y1;
        px[i*2+1].rig= px[i*2].rig= y2;
        px[i*2].pos= x1;
        px[i*2].flag= 1;
        px[i*2+1].pos= x2;
        px[i*2+1].flag= -1;
        py[i*2+1].lef= py[i*2].lef= x1;
        py[i*2+1].rig= py[i*2].rig= x2;
        py[i*2].pos= y1;
        py[i*2].flag= 1;
        py[i*2+1].pos= y2;
        py[i*2+1].flag= -1;
        disx[i*2]= x1;
        disx[i*2+1]= x2;
        disy[i*2]= y1;
        disy[i*2+1]= y2;
    }
}
bool cmp( point x, point y){
    return x.pos < y.pos;
}
void disperse(){
    sort( disx, disx+ 2*n);
    sort( disy, disy+ 2*n);
    lenx= unique(disx, disx+ 2*n)- disx;
    leny= unique(disy, disy+ 2*n)- disy;
    sort( px, px+ 2*n, cmp);     //对y做线段树,按pos(即x)顺序由小及大排列
    sort( py, py+ 2*n, cmp);
}
void maketree( int num, int lef, int rig){
    seg[num].lef= lef;
    seg[num].rig= rig;
    seg[num].mid= (lef + rig) >> 1;
    seg[num].cov= 0;
    if( lef +1!= rig){
        maketree( num*2, lef, seg[num].mid);
        maketree( num*2+ 1, seg[num].mid, rig);
    }
}
//1. 当seg[num].cov由0变1时,记录线段长度,并乘2,即上下对应两边长度和
//2. 对于seg[num].cov== 0且 左右点相符, 同时为新加线段,而非已有线段向下更新时,计起长度
//3. 若左右端点相符,且seg[num].cov>0时, seg[num].cov+= cov
//4. 若左右端点相符,seg[num].cov==0,并且为已有线段向下更新,而非新加入线段,seg[num].cov+= cov
//5. 对于某条seg[num].cov>0的线段,若要更新其一部分,即子线段,则先把这条线段的
//   seg[num].cov更新到其所有子节点,再把seg[num].cov赋为-1, 再对其子线段进行更新
//6. tt 为1时,为新加线段,tt为0时,是已有线段向下更新
//7. tmp为1,对y建的线段树,tmp为0, 对x建的线段树
//8. 已经标记为-1的线段,不会再变为其他值
int insert( int num, int lef, int rig, int cov, int tt, int tmp){
    if( seg[num].cov == 0 && seg[num].lef== lef && seg[num].rig== rig  && tt== 1){
        seg[num].cov= 1;
        if( tmp== 1)return disy[rig]- disy[lef];
        else return disx[rig]-disx[lef];
    }
    if( seg[num].cov >= 0 && seg[num].lef == lef && seg[num].rig== rig){
        seg[num].cov+= cov;
        return 0;
    }
    if( seg[num].cov > 0){
        insert( num*2, seg[num].lef, seg[num].mid, seg[num].cov, 0, tmp);
        insert( num*2+1, seg[num].mid, seg[num].rig, seg[num].cov, 0,  tmp);
    }
    seg[num].cov= -1;
    if( rig <= seg[num].mid)
        return insert( num*2, lef, rig, cov, tt, tmp);
    else if( lef >= seg[num].mid)
        return insert( num*2+1, lef, rig, cov, tt, tmp);
    else return insert( num*2, lef, seg[num].mid, cov, tt, tmp) + insert( num*2+1, seg[num].mid, rig, cov, tt, tmp);
}
int bx(int x){   //查找x的离散后坐标
    int lef= 0, rig= lenx-1, mid;
    while( lef <= rig){
        mid= (lef + rig) >> 1;
        if( disx[mid] < x) lef= mid+1;
        else if( disx[mid] > x) rig= mid- 1;
        else return mid;
    }
}
int by(int y){
    int lef= 0, rig= leny-1, mid;
    while( lef <= rig){
        mid= (lef + rig) >> 1;
        if( disy[mid] < y) lef= mid+1;
        else if( disy[mid] > y) rig= mid- 1;
        else return mid;
    }
}
int main(){
   // freopen("1.txt", "r", stdin);
    int i, aa;
    while( scanf("%d", &n) != EOF){
        init();
        disperse();
        ans= 0;
        //分别对对x、y 扫描线, 求长度并
        maketree(1, 0, leny);
        for( i=0; i<2*n; i++){
            ans+= insert(1,  by(px[i].lef), by(px[i].rig), px[i].flag, 1,  1);
        }
        maketree(1, 0, lenx);
        for( i=0; i<2*n; i++){
            ans+= insert(1,  bx(py[i].lef), bx(py[i].rig), py[i].flag, 1, 0);
        }
        printf("%d\n", ans*2);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值