zoukankan      html  css  js  c++  java
  • [HNOI2012]三角形覆盖问题

    题面

    二维平面中,给定 (N) 个等腰直角三角形(每个三角形的两条直角边分别平行于坐标轴,斜边从左上到右下)。我们用三个非负整数 ((x, y, d)) 来描述这样一个三角形,三角形三个顶点的坐标分别为 ((x, y), (x + d, y))((x, y + d)) 。要求计算这 (N) 个三角形所覆盖的总面积。例如,下图有 (3) 个三角形,覆盖的总面积为 11.0。

    输入格式:

    输入文件第一行为一个正整数N,表示三角形的个数。

    接下来的 (N) 行每行有用空格隔开的三个非负整数,(x, y, d) ,描述一个三角形的顶点坐标,分别为 ((x, y)) , ((x + d, y)) , (( x, y+d)) ,其中 (x, y, d) 满足 (0<= x, y, d<=1000000)

    输出格式:

    仅包含一行,为一个实数 (S) ,表示所有三角形所覆盖的总面积,输出恰好保留一位小数。输入数据保证 (Sle 2^{31})

    输入样例

    3
    1 1 4
    2 0 2
    3 2 2
    

    输出样例

    11.0
    

    (Solution:)

    显然扫描线,扫描线的做法因题而异,不同的题面有不同的写法。

    这里给出链表+扫描线的方法:

    先按 (y) 轴排序,然后从下扫描到上,因为坐标都是小于1e6的,所以直接暴力扫。

    这题跟矩形面积并不一样,因为是等腰直角三角形,每次扫描线向上走一个单位,扫描线对应的地方覆盖就要少一。

    数据结构:

    1. 双向链表

    实际上是一个容器,存的是覆盖当前扫描线的三角形的编号,即如果编号为 (i) 的三角形覆盖了扫描线的一部分,那么 (list[i]) 就在链表中。
    链表只是为了我们快速修改信息,插入和删除都是 (O(1)) 的, 查询信息也很方便。

    1. (cover[x])

    存储 ( (x) , 扫描线位置) 被多少个三角形覆盖,用来更新扫描线被覆盖的线段长度用。

    算法流程:

    1. (y) 轴排序。
    2. 从下往上扫描 (i) 记录扫描线的位置,(j) 记录当前有前 (j) 个在链表中或者已经处理完。
    3. 先统计链表中的答案 (now) ,并修改信息,记下 (i-1) 时的覆盖线段长,(ans+= frac{now+last}{2}).
    4. 将新的三角形插进链表,更新 (cover) ,求出新的被覆盖线段长,记录到 (last) ,扫描线上移,执行 (3) 直至扫描完成。
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
     
    using namespace std;
     
    const int N = 1e5 + 20;
     
    int n, mx;
    struct Tri
    {
        int x, y, d, l, r;
     
        Tri() {}
        Tri(const int &_x, const int &_y, const int &_d) 
        { x = _x, y = _y, d = _d, l = _x, r = _x + _d - 1;} 
     
    } tri[N];
    inline bool cmp(const Tri &A, const Tri &B)
    { return A.y < B.y; }
     
    namespace List 
    {
        int head, tail, nxt[N], pre[N];
     
        void Del(int x) 
        {
            pre[nxt[x]] = pre[x];
            nxt[pre[x]] = nxt[x];
        }
         
        void Ins(int x, int y)
        {
            pre[nxt[x]] = y;
            nxt[y] = nxt[x];
            nxt[x] = y;
            pre[y] = x;
        }
     
        bool ins(int x)
        {
            if (tri[x].d == 0) return false;
            Ins(head, x);
            return true;
        }
    }
    using namespace List;
     
    int cover[(int)2e6 + 2];
     
    int main() 
    {
     
        cin >> n;
        for (int i = 1; i <= n; ++ i)
        {
            int x, y, d;
            cin >> x >> y >> d;
            mx = max(mx, y + d);
            tri[i] = Tri(x, y, d);
        }
        sort(tri + 1, tri + 1 + n, cmp);
     
        head = 0; tail = n + 1;
        nxt[head] = tail; pre[tail] = head;
        int ans = 0, last = 0, now = 0;
        for (int i = tri[1].y, j = 1; i <= mx; ++ i)
        {
            now = last;
            for (int k = nxt[head]; k != tail; k = nxt[k]) 
            {
                -- cover[tri[k].r];
                if (!cover[tri[k].r]) now--;
                tri[k].r --;
                if (tri[k].x > tri[k].r) Del(k);
            }
            ans += now + last;
            while (j <= n && tri[j].y == i) 
            {
                if (ins(j)) 
                {
                    for (int k = tri[j].x; k < tri[j].x + tri[j].d; k ++)
                    {
                        if (!cover[k]) now ++;
                        cover[k] ++;
                    }
                }
                j ++;
            }
            last = now;
        }
        printf("%.1f
    ", ans / 2.0);
    }
    
    
  • 相关阅读:
    自己实现的一个简单的C# IOC 容器
    C# 内存缓存工具类 MemoryCacheUtil
    使用触发器和C#程序实现数据同步
    Maven 命令安装指定 jar 包到本地仓库
    C# RSA 非对称加密
    JS可选链操作符?.和双问号??
    Learn D3 入门文档: Introduction
    Lerna 基本概念
    图片 src 为二进制的处理
    ASCII 和 Base64
  • 原文地址:https://www.cnblogs.com/cnyali-Tea/p/10502919.html
Copyright © 2011-2022 走看看