杭电1255覆盖的面积(线段树+离散化+线扫描)

->题目请戳这里<-

题目大意:中文题,不解释。

题目分析:求矩形面积并,线段树搞之。但是这题不是简单求面积并,而是求覆盖2次及以上的面积并。一开始直接写个二维线段树,尽快已经尽量做到节约空间,最后还是爆了,1000个矩形,二维线段树伤不起。。。然后换个思路,换成一维线段树+线扫描,仿照之前写的一个面积并的题目,结果又是各种跪。然后分析了一下原因,因为我一开始所谓的线扫描法简直弱爆了,那就不叫线扫描,只是看着像而已,我是把每个矩形分成一段一段的插入,这样的话,一个矩形如果x方向上距离足够大的话,所产生的扫描线数量就会很多,n个的话就更多了。学习了别人的代码后发现,其实n个矩形只要2n条扫描线就够了,不过要记得矩形的左右界,左界为1,右界为-1,那么每个矩形有2条扫描线,直接往线段树里面插,如果扫到了一个矩形的左界,那么这个矩形就插到线段树中,更新每个节点的y方向上的长度并,如果扫到一个矩形的右界,那么将-1插入线段树,就相当于将这个矩形删除了。

不过这题的难点在于要求覆盖至少2次的面积并,搜了几个代码基本都一样,也解释的不是很清楚,所以这里尽量解释清楚些。

线段树的每个节点我们需要用到3个变量:flag:记录当前区间覆盖的矩形数;one:记录当前区间只覆盖了一次的长度;len:记录当前区间覆盖了2次及2次以上的长度。这里注意one表示的长度和len表示有效长度是没有交集的。

flag值有3种情况:1:flag = 0,表示当前区间没有完全覆盖,注意没有完全覆盖并不表示当前区间没有矩形覆盖,只是没有恰好完全覆盖当前区间的矩形,也许当前区间的子区间就有覆盖的矩形;2:flag = 1,表示当前区间完全被矩形覆盖了1次;3:flag >1,表示当前区间被完全覆盖2次及以上,那么这个区间整个长度就是我们要的。

对于one,当前区间恰好覆盖了一次的情况,那么当前区间满足覆盖了2次及以上的长度就分2部分,第一部分是当前区间子区间中恰好覆盖了一次的长度,即左右子区间中one的长度,因为当前区间恰好被完全覆盖了一次,而子区间中也有恰好被覆盖了1次的,那么子区间中恰好被覆盖了一次的实际上已经被覆盖2次了;第二部分就是当前区间左右子区间中已经被覆盖了2次及以上的区间,这2部分构成了当前区间的有效长度len。

我们插入一条扫描线的时候,如果找到当前区间就是我们要插入的区间,那么这个区间覆盖次数+flag(该扫描线边界,左边界为1,右边界为-1,+1就说明加入了一个矩形,-1说明这个矩形已经在扫描线左边了,删之)。每插入一条扫描线,更新节点的有效长度值,直到根节点。最后算出面积并。

ps:注意这题题目描述有误,给的数据点是坐下角坐标和右上角坐标,不是题目描述的左上角和右下角坐标。

pps:这题跪了好久啊,差点要吐血了,总算还是过了。。。

ppps:还是太弱了啊。。。

详情请见代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1005;
double hash[N<<2];
struct node
{
    double x,up,down;
    int flag;
}line[N<<2];
struct nd
{
    int flag;
    double one;//当前区间恰好覆盖一次的长度
    double len;//当前区间覆盖2+次的长度,有效长度
}tree[N<<4];

int cmp(struct node a,struct node b)
{
    return a.x < b.x;
}

void build(int num,int s,int e)
{
    tree[num].flag = 0;
    tree[num].len = tree[num].one = 0;
    if(s == e)
        return;
    int mid = (s + e)>>1;
    build(num<<1,s,mid);
    build(num<<1|1,mid + 1,e);
}

void cal_len(int num,int s,int e)//计算当前区间长度
{
    if(tree[num].flag > 1)//当前区间被覆盖2+次,整个区间全部有效
    {
        tree[num].len = hash[e + 1] - hash[s];
        tree[num].one = 0;
        return;
    }
    else
    {
        if(tree[num].flag == 1)//当前区间恰好被覆盖一次
        {
            if(s == e)
                tree[num].len = 0;
            else
                tree[num].len = tree[num<<1].len + tree[num<<1|1].len + tree[num<<1].one + tree[num<<1|1].one;//有效长度是子区间有效长度+子区间已经被恰好覆盖一次的长度
            tree[num].one = hash[e + 1] - hash[s] - tree[num].len;//因为该区间被恰好覆盖了一次,所以当前区间被覆盖至少1+次,那么当前区间恰好被覆盖一次的就这么多了。
        }
        else//当前区间没有被完整的覆盖过,那么有效区间就是子区间有效区间
        {
            if(s == e)
                tree[num].len = tree[num].one = 0;
            else
            {
                tree[num].one = tree[num<<1].one + tree[num<<1|1].one;
                tree[num].len = tree[num<<1].len + tree[num<<1|1].len;
            }
        }
    }
}

void insert(int num,int s,int e,int l,int r,int add)
{
    if(s == l && e == r)
    {
        tree[num].flag += add;
        cal_len(num,s,e);
        return;
    }
    int mid = (s + e)>>1;
    if(r <= mid)
        insert(num<<1,s,mid,l,r,add);
    else
    {
        if(l > mid)
            insert(num<<1|1,mid + 1,e,l,r,add);
        else
        {
            insert(num<<1,s,mid,l,mid,add);
            insert(num<<1|1,mid + 1,e,mid + 1,r,add);
        }
    }
    cal_len(num,s,e);
}

int getind(double x,int len)
{
    int l,r,mid;
    l = 1;
    r = len;
    while(l <= r)
    {
        mid = (l + r)>>1;
        if(hash[mid] == x)
            return mid;
        else
        {
            if(hash[mid] > x)
                r = mid - 1;
            else
                l = mid + 1;
        }
    }
    return -1;
}

void print(int num,int s,int e)
{
	printf("num:%d  %d  %lf  %lf\n",num,tree[num].flag,tree[num].one,tree[num].len);
    if(s == e)
    {
        return;
    }
    int mid = (s + e)>>1;
    print(num<<1,s,mid);
    print(num<<1|1,mid + 1,e);
}

int main()
{
    double x1,x2,y2,y1;
    int i,t,n,m;
    scanf("%d",&t);
    while(t --)
    {
        scanf("%d",&n);
        int y = 1;
        for(i = 1;i < n + n;i += 2)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            line[i].x = x1;
            line[i].up = y2;
            line[i].down = y1;
            line[i].flag = 1;
            line[i + 1].x = x2;
            line[i + 1].up = y2;
            line[i + 1].down = y1;
            line[i + 1].flag = -1;
            hash[y ++] = y1;
            hash[y ++] = y2;
        }
        sort(hash + 1,hash + y);
        m = 2;
        for(i = 2;i < y;i ++)
            if(hash[i] != hash[i - 1])
                hash[m ++] = hash[i];
        m --;
        build(1,1,m);
        sort(line + 1,line + n + n + 1,cmp);
        double ans = 0;
        int l,r;
        l = getind(line[1].down,m);
        r = getind(line[1].up,m);
        insert(1,1,m,l,r - 1,line[1].flag);
        for(i = 2;i <= n + n;i ++)
        {
            ans += (line[i].x - line[i - 1].x) * tree[1].len;
            l = getind(line[i].down,m);
            r = getind(line[i].up,m);
            insert(1,1,m,l,r - 1,line[i].flag);
           // print(1,1,m);
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}
//359MS	500K

你可能感兴趣的:(数据结构,线段树)