POJ1389 掃描線+線段樹

一道經典的掃描線+線段樹求矩形面積並的題目

用線段樹維護y,記錄所有線段被覆蓋的次數和長度,掃描線維護x,把一個長方形拆成兩個事件,左邊界作爲+1,右邊界作爲-1,即可利用線段樹進行維護。

 

非離散化版本:

#include<iostream>
#include<algorithm>
using namespace std;
#define lson k<<1
#define rson k<<1|1
const int maxn=1005;
const int maxm=50005;
struct event
{
    int x,y1,y2,add;
}eve[maxn<<1];
bool cmp(const event &a,const event &b)
{
    return a.x<b.x;
}
struct node
{
    int sum,cnt;
}tree[maxm<<2];
void pushup(int k,int l,int r)
{
    tree[k].sum=tree[lson].sum+tree[rson].sum;
    if(tree[k].cnt)
        tree[k].sum=r-l;
}
void update(int k,int l,int r,int L,int R,int v)
{
    if(L<=l&&r<=R)
    {
        tree[k].cnt+=v;
        pushup(k,l,r);
        return ;
    }
    int mid=l+r>>1;
    if(R<=mid)
        update(lson,l,mid,L,R,v);
    else if(L>=mid)
        update(rson,mid,r,L,R,v);
    else
    {
        update(lson,l,mid,L,mid,v);
        update(rson,mid,r,mid,R,v);
    }
    pushup(k,l,r);
}
int main()
{
    ios_base::sync_with_stdio(0);
    int x1,x2,y1,y2;
    while(cin>>x1>>y1>>x2>>y2)
    {
        if(x1+x2+y1+y2==-4)
            break;
        int all=0;
        int n=0;
        while(x1+x2+y1+y2!=-4)
        {
            n=max(n,y2);
            eve[++all].x=x1,eve[all].y1=y1,eve[all].y2=y2,eve[all].add=1;
            eve[++all].x=x2,eve[all].y1=y1,eve[all].y2=y2,eve[all].add=-1;
            cin>>x1>>y1>>x2>>y2;
        }
        sort(eve+1,eve+all+1,cmp);
        update(1,0,n,eve[1].y1,eve[1].y2,eve[1].add);
        long long ans=0;
        for(int i=2;i<=all;i++)
        {
            ans+=1ll*(eve[i].x-eve[i-1].x)*tree[1].sum;
            update(1,0,n,eve[i].y1,eve[i].y2,eve[i].add);
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

離散化版本:

#include<iostream>
#include<algorithm>
using namespace std;
#define lson k<<1
#define rson k<<1|1
const int maxn=1005;
const int maxm=50005;
struct event
{
    int x,y1,y2,add;
}eve[maxn<<1];
bool cmp(const event &a,const event &b)
{
    return a.x<b.x;
}
struct node
{
    int sum,cnt;
}tree[maxm<<2];
struct rectangle
{
    int x1,y1,x2,y2;
}rec[maxn];
int id[maxn<<1];
void pushup(int k,int l,int r)
{
    tree[k].sum=tree[lson].sum+tree[rson].sum;
    if(tree[k].cnt)
        tree[k].sum=id[r]-id[l];
}
void update(int k,int l,int r,int L,int R,int v)
{
    if(L<=l&&r<=R)
    {
        tree[k].cnt+=v;
        pushup(k,l,r);
        return ;
    }
    int mid=l+r>>1;
    if(R<=mid)
        update(lson,l,mid,L,R,v);
    else if(L>=mid)
        update(rson,mid,r,L,R,v);
    else
    {
        update(lson,l,mid,L,mid,v);
        update(rson,mid,r,mid,R,v);
    }
    pushup(k,l,r);
}
int main()
{
    ios_base::sync_with_stdio(0);
    int x1,x2,y1,y2;
    while(cin>>x1>>y1>>x2>>y2)
    {
        if(x1+x2+y1+y2==-4)
            break;
        int all=0;
        while(x1+x2+y1+y2!=-4)
        {
            rec[++all].x1=x1,rec[all].x2=x2,rec[all].y1=y1,rec[all].y2=y2;
            cin>>x1>>y1>>x2>>y2;
        }
        for(int i=1;i<=all;i++)
        {
            id[i]=rec[i].y1,id[i+all]=rec[i].y2;
        }
        sort(id+1,id+2*all+1);
        for(int i=1;i<=all;i++)
        {
            rec[i].y1=lower_bound(id+1,id+2*all+1,rec[i].y1)-id;
            rec[i].y2=lower_bound(id+1,id+2*all+1,rec[i].y2)-id;
            eve[i].add=1,eve[i+all].add=-1;
            eve[i].x=rec[i].x1,eve[i+all].x=rec[i].x2;
            eve[i].y1=eve[i+all].y1=rec[i].y1;
            eve[i].y2=eve[i+all].y2=rec[i].y2;
        }
        sort(eve+1,eve+2*all+1,cmp);
        update(1,1,2*all,eve[1].y1,eve[1].y2,eve[1].add);
        long long ans=0;
        for(int i=2;i<=2*all;i++)
        {
            ans+=1ll*(eve[i].x-eve[i-1].x)*tree[1].sum;
            update(1,1,2*all,eve[i].y1,eve[i].y2,eve[i].add);
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

其實是否離散化對於線段樹來說幾乎沒有區別,此處維護線段長度的線段樹與普通維護區間的線段樹不太一樣,在進行分治的時候需要特別注意參數的傳遞。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章