hdu 1828 Picture【扫描线求周长模板题】

上周在做ac自动机+dp这种混合的题时,就超不爽的,这周居然要做线段树+几何的题,我的内心上崩溃的o(>﹏<)o 模板题,既然不是自己做的,总还是要讲讲扫描线是个什么玩意==

扫描线,顾名思义,就是用一条线捋着各个边从下到上过一遍,每遍历一个高度的边,累加长度:点击打开链接

计算横线部分的方法现在这次总区间被覆盖的长度和上一次总区间被覆盖的长度的差的绝对值:fabs(a[1].len - pre)

计算竖线部分的方法:我们要知道添加了这条横线后会有多少条竖线,答案是2*sum,所以为什么要记录sum呢,因为总区间被sum条线段覆盖,那么必定有2*sum的端点,这些端点其实就是连着竖线,那么这些竖线的长度是多少呢?

就是【下一条横线的高度-现在这条横线的高度】,只要把这个高度乘上2*sum即可:(ege[i+1].x - ege[i].x)*a[1].sum * 2

模板:点击打开链接

/**************
2016.3.21
poj 1177 & hdu 1828 
**************/
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;

#define Lson r<<1
#define Rson r<<1|1

const int MAXN = 2e5+5;
const int oo = 1e9+7;

struct stgmentTree
{///len 保存区间包含的边的长度,cover 保存区间被覆盖的次数, sum保存有多少个不相连的区间段
    int L, R, len, cover, sum;
    bool lb, rb;///左边和右边是否被覆盖
    int mid(){return (R+L)>>1;}
}a[MAXN<<2];
///dir 等于 1 的时候代表左边,-1时候代表右边,右边会抵消左边
struct Point{int x, y1, y2, dir;}ege[MAXN];
int Hash[MAXN], nh;///保存离散化后的数据,nh表示元素个数

bool cmp(Point n1, Point n2)
{///把边按照x的值从左往右排序
    return n1.x < n2.x;
}
///查找一条边的长度, x1 < x2
int findSegLen(int x1, int x2)
{///用下标可以直接检索值
    return Hash[x2] - Hash[x1];
}
void buildTree(int r, int L, int R)
{
    a[r].L = L, a[r].R = R, a[r].cover=0;

    if(L == R-1)return ;

    buildTree(Lson, L, a[r].mid());
    buildTree(Rson, a[r].mid(), R);
}
void pushUp(int r)///合并操作
{///需要先注意本区间是否被覆盖
    if(a[r].cover != 0)
    {
        a[r].len= findSegLen( a[r].L, a[r].R );
        a[r].sum = a[r].lb = a[r].rb = 1;
    }
    else if(a[r].L == a[r].R - 1)
    {
        a[r].len = 0;
        a[r].sum = a[r].lb = a[r].rb = 0;
    }
    else
    {///如果本区间未被覆盖,并且不为叶子节点,那么就等于子区间的和
        a[r].len = a[Lson].len + a[Rson].len;

        a[r].lb = a[Lson].lb, a[r].rb = a[Rson].rb;
        a[r].sum = a[Lson].sum + a[Rson].sum - (a[Lson].rb & a[Rson].lb);
    }

}
void upData(int r, int L, int R, int dir)
{
    if( a[r].L == L && a[r].R == R )
    {
        a[r].cover += dir;
        pushUp(r);

        return ;
    }

    if(R <= a[r].mid())
        upData(Lson, L, R, dir);
    else if(L >= a[r].mid())
        upData(Rson, L, R, dir);
    else
    {
        upData(Lson, L, a[r].mid(), dir);
        upData(Rson, a[r].mid(), R, dir);
    }

    pushUp(r);
}

int main()
{
    int N;

    while(scanf("%d", &N) != EOF)
    {
        int i, x1, x2, y1, y2, k=0, C=0, pre=0; nh = 0;

        for(i=0; i<N; i++)
        {
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            ege[k].x=x1, ege[k].y1=y1, ege[k].y2=y2, ege[k++].dir=1;///左边y
            ege[k].x=x2, ege[k].y1=y1, ege[k].y2=y2, ege[k++].dir=-1;///右边y
            Hash[nh++] = y1, Hash[nh++] = y2;
        }

        sort(Hash, Hash+nh);///排序去重复
        nh = unique(Hash, Hash+nh) - Hash;
        ///离散化后的数据是从0下标开始的
        buildTree(1, 0, nh-1);

        ///对边按照x排序
        sort(ege, ege+k, cmp);

        for(i=0; i<k-1; i++)
        {
            int L = lower_bound(Hash, Hash+nh, ege[i].y1) - Hash;
            int R = lower_bound(Hash, Hash+nh, ege[i].y2) - Hash;

            upData(1, L, R, ege[i].dir);

            C += fabs(a[1].len - pre) + (ege[i+1].x - ege[i].x)*a[1].sum * 2;
            pre = a[1].len;
        }

        printf("%d\n", C+pre);
    }

    return 0; 

}



你可能感兴趣的:(hdu 1828 Picture【扫描线求周长模板题】)