POJ-1177 Picture(线段树+扫描线)

链接:POJ-1177 Picture

题意

给出平面直角坐标系上 n n n 0 ≤ n ≤ 5000 0\le n\le 5000 0n5000)个矩形,求所有矩形合并后的所有线段长度之和。



分析

以计算 水平线段(平行于 x x x轴)的长度之和 为例,竖直扫描线(平行于 y y y轴)水平扫过 各矩形的 左、右边界

可以发现,每扫过单位水平长度,水平线段长度之和应当增加 当前竖直方向的线段数目 ∗ 2 *2 2

故要用线段树维护竖直方向的覆盖线段数目,较为特殊的一点是,虽然是区间修改,但是并不需要下传延迟标记(lazy tag),因为

  1. 遍历同一矩形的左、右边界时分别需要加入、删除该覆盖,故线段树的增加和删除操作是成对出现的
  2. 我们只需要知道整体的覆盖线段数目,即线段树根节点的值,无需进一步区间查询操作

随后计算 竖直线段(平行于 y y y轴)的长度之和 ,做相同处理即可;

此外,要先对数据做离散化处理,不再用坐标,而是用第几号线段至第几号线段来表示边界,更便于处理,详见代码。



代码

#include
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int maxn=1e4+10;
int n,X[maxn],N,Y[maxn],M;
struct rectangle
{
    int a,b,c,d;
}r[maxn];
struct border
{
    int l,r;       //该边界由线段l~r组成
    bool tag;      //tag=1,左边界/下边界;tag=0,右边界/上边界
};
vector<border> x[maxn],y[maxn];
void prework()     //离散化
{
    sort(X+1,X+N+1);
    sort(Y+1,Y+M+1);
    N=unique(X+1,X+N+1)-(X+1);
    M=unique(Y+1,Y+M+1)-(Y+1);
    for(int i=1;i<=n;i++)
    {
        r[i].a=lower_bound(X+1,X+N+1,r[i].a)-X;
        r[i].c=lower_bound(X+1,X+N+1,r[i].c)-X;
        r[i].b=lower_bound(Y+1,Y+M+1,r[i].b)-Y;
        r[i].d=lower_bound(Y+1,Y+M+1,r[i].d)-Y;

        x[r[i].a].push_back(border{r[i].b,r[i].d-1,1});
        x[r[i].c].push_back(border{r[i].b,r[i].d-1,0});
        y[r[i].b].push_back(border{r[i].a,r[i].c-1,1});
        y[r[i].d].push_back(border{r[i].a,r[i].c-1,0});
    }
}
struct node
{
    int num;    //该区间包含的线段数
    int cnt;    //该区间被直接整体覆盖的次数(相当于不下传的延迟标记)
    int ltag;   //该区间最左端有无被覆盖
    int rtag;   //该区间最右端有无被覆盖
}t[maxn<<2];
void push_up(int rt)
{
    if(t[rt].cnt>0)    //该区间被直接整体覆盖
    {
        t[rt].num=1;
        t[rt].ltag=t[rt].rtag=1;
    }
    else     //未被直接整体覆盖
    {
        t[rt].num=t[rt<<1].num+t[rt<<1|1].num;   //左子树线段数+右子树线段数
        if(t[rt<<1].rtag&&t[rt<<1|1].ltag)
            t[rt].num--;
        t[rt].ltag=t[rt<<1].ltag;
        t[rt].rtag=t[rt<<1|1].rtag;
    }
}
void updata(int rt,int l,int r,int ql,int qr,int op)    //op=1:增加矩阵
{                                                       //op=0:删除矩阵
    if(ql<=l&&r<=qr)
    {
        if(op)
            t[rt].cnt++;
        else
            t[rt].cnt--;
        push_up(rt);
        return;
    }
    int mid=(l+r)>>1;
    if(ql<=mid)
        updata(rt<<1,l,mid,ql,qr,op);
    if(qr>mid)
        updata(rt<<1|1,mid+1,r,ql,qr,op);
    push_up(rt);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d %d %d",&r[i].a,&r[i].b,&r[i].c,&r[i].d);
        X[++N]=r[i].a;X[++N]=r[i].c;
        Y[++M]=r[i].b;Y[++M]=r[i].d;
    }
    prework();
    int ans=0;
    for(int i=1;i<=N;i++)
    {
        if(i>1)
            ans+=(X[i]-X[i-1])*(t[1].num*2);
        for(auto b:x[i])
            updata(1,1,M-1,b.l,b.r,b.tag);
    }
    memset(t,0,sizeof(t));
    for(int i=1;i<=M;i++)
    {
        if(i>1)
            ans+=(Y[i]-Y[i-1])*(t[1].num*2);
        for(auto b:y[i])
            updata(1,1,N-1,b.l,b.r,b.tag);
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(★水题之路,★数据结构,#,【线段树】)