POJ 1177 Picture [离散化+扫描线+线段树]

http://poj.org/problem?id=1177
给若干矩形,求被覆盖的区域的周长。

y 坐标离散化后,按 x 坐标进行扫描。用线段树维护两个东西,当前竖线的叠加长度 len 和 条数 cnt 。 前一个用来计算竖直方向的周长部分,后一个用来计算水平方向的。
left[node]right[node] 来记录每个结点左端和右端是否被覆盖,用来维护 cnt

重叠的边也不能计算,这反应在对扫描线的排序上,两线重叠时入边应在出边之前。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;

#define CHD int lc = node<<1,rc = node<<1|1;
#define MID int mid = (L+R)>>1;
#define debug(x) cout<<"debug "<<x<<endl;
#define rep(i,f,t) for(int i = (f),_end =(t); i <= _end; ++i)

struct Node{
    int x;
    int y1,y2;
    int flag; //入边为1,出边为-1
    Node(int x,int y1,int y2,int f)
        :x(x),y1(y1),y2(y2),flag(f){
        }
    bool operator< (const Node &n2)const{
        if(x == n2.x)return flag > n2.flag;//入边在出边前
        return x < n2.x;
    }
};
vector<Node> line;
vector<int> vs;
const int maxn = 10005;

struct sgt{
    int len[maxn<<2];
    int cnt[maxn<<2];
    int cov[maxn<<2];
    int left[maxn<<2];
    int right[maxn<<2];

    void maintain(int node,int L,int R){
        if(cov[node] > 0){
            len[node] = vs[R]-vs[L-1];
            cnt[node] = 1;
            left[node] = right[node] = 1;
        } else {
            if(L == R){
                len[node] = 0;
                cnt[node] = 0;
                left[node] = right[node] = 0;
            }else{
                CHD;
                len[node] = len[lc]+len[rc];
                cnt[node] = cnt[lc]+cnt[rc]-(left[rc]&right[lc]);
                left[node] = left[lc];
                right[node] = right[rc];
            }
        }
    }
    void update(int from,int to,int val,int node,int L,int R){
        if(from <= L && R <= to){
            cov[node] += val;
        } else {
            MID;CHD;
            if(from <= mid) update(from,to,val,lc,L,mid);
            else    maintain(lc,L,mid);
            if(to > mid) update(from,to,val,rc,mid+1,R);
            else    maintain(rc,mid+1,R);
        }
        maintain(node,L,R);
    }

    int solve(){
        int ans = 0,ans2 = 0;//竖直和水平方向的周长
        int n = vs.size()-1;
        int last = 0;
        rep(i,0,line.size()-1){
            int x = line[i].x;
            int f = line[i].y1+1;
            int t = line[i].y2;
            if(i > 0){
                int tmp = cnt[1] * (x-line[i-1].x);
                ans2 += tmp*2;
            }
            update(f,t,line[i].flag,1,1,n);
            ans += abs(last-len[1]);//竖直方向长度变化
            last = len[1];
        }
        return ans+ans2;
    }
}tree;


void pre(){
    sort(vs.begin(),vs.end());
    vs.erase(unique(vs.begin(),vs.end()),vs.end());
    rep(i,0,line.size()-1){
        line[i].y1 = lower_bound(vs.begin(),vs.end(),line[i].y1) - vs.begin();
        line[i].y2 = lower_bound(vs.begin(),vs.end(),line[i].y2) - vs.begin();
    }
    sort(line.begin(),line.end());
}

int main(){
    int n;
    scanf("%d",&n);
    if(n == 0){
        printf("0\n");
        return 0;
    }
    line.reserve(n<<1);
    vs.reserve(n<<1);
    rep(i,1,n){
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        vs.push_back(y1);
        vs.push_back(y2);
        line.push_back(Node(x1,y1,y2,1));
        line.push_back(Node(x2,y1,y2,-1));
    }
    pre();
    int ans = tree.solve();
    printf("%d\n",ans);
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

posted @ 2015-05-01 11:51  DSChan  阅读(131)  评论(0编辑  收藏  举报