POJ 1436 - Horizontally Visible Segments

Advanced Data Structures :: Segment Tree


Description

平面上有几条线段,这些线段都是平行于 y 轴的。

如果存在一条平行于 x 轴的直线能够与两条线段相交,

并且直线在两个交点之间的部分,没有与其他线段相交。

则称这两条线段相互“可视”。

三条线段可以成为一个组合,如果组合内的三条线段两两“可视”,

则该组线段形成了一个特殊的三角形。

告诉你平面上所有线段的x、y1、y2 坐标(线段为 (x, y1) 到 (x, y2) ),

问有多少个这样的三角形组合。

题目保证没有线段相交于同一点。


Type

Advanced Data Structures :: Segment Tree


Analysis

我们将所有线段画在纸上,然后逆时针旋转90度来看。

这题有点像铺地毯(或者说贴海报)似的,一层覆盖一层。

由此,可以想到利用线段树来求解。


把所有线段按 x 坐标排序,然后按顺序添加到线段树中。

而一条线段“可视”的线段中所有 x 坐标比它小的线段,正好是被它覆盖到的线段(不一定完全覆盖)。

至于 x 坐标比它大的,因为“可视”是相互的,后面的线段肯定可以发现这组“可视”的关系。


这样,我们就可以在查询线段树的时候,用一个 vector 数组来存储某条线“可视”的 x 坐标比它小的线段。

然后好像也没有什么比较好的算法,就只好暴力求解(复杂度O(n^4))有多少组特殊的三角形了。


其中有几点要注意的:

  • 离散化问题
线段树上,由于区间内,点和线都有意义,所以应该将区间倍增,以保证点线不会相互影响。
只要在输入时,将 y 坐标的值自乘 2 就可以了。
  • 成段更新
更新线段树时,要注意成段更新。但是这道题的要求不像其他成段更新题目那样高。
在询问的时候,依然可以并且也需要询问到底,不会超时。

Solution
// POJ 1436
// Horizontally Visible Segments
// by A Code Rabbit

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

#define LSon(x) ((x) << 1)
#define RSon(x) ((x) << 1 | 1)

const int MAXN = 8002;
const int ROOT = 1;

struct Seg {
    int w;
};

struct SegTree {
    Seg node[MAXN * 2 << 2];
    void Build() { memset(node, -1, sizeof(node)); }
    void Push(int pos) {
        if (node[pos].w != -1) {
            node[LSon(pos)].w = node[RSon(pos)].w = node[pos].w;
            node[pos].w = -1;
        }
    }
    void Modify(int l, int r, int pos, int x, int y, int z) {
        if (x <= l && r <= y) {
            node[pos].w = z;
            return;
        }
        Push(pos);
        int m = l + r >> 1;
        if (x <= m) Modify(l, m, LSon(pos), x, y, z);
        if (y > m) Modify(m + 1, r, RSon(pos), x, y, z);
    }
    void Query(int l, int r, int pos, int x, int y, int z, vector<int>* vec, bool* bo) {
        if (node[pos].w != -1) {
            if (!bo[node[pos].w]) {
                vec[z].push_back(node[pos].w);
                bo[node[pos].w] = true;
            }
            return;
        }
        if (l == r) return;
        int m = l + r >> 1;
        if (x <= m) Query(l, m, LSon(pos), x, y, z, vec, bo);
        if (y > m) Query(m + 1, r, RSon(pos), x, y, z, vec, bo);
    }
};

struct Segment {
    int y1, y2;
    int x0;
};

int n;
Segment seg[MAXN];

vector<int> vec[MAXN];
bool bo[MAXN];
SegTree tree;

int Cmp(Segment a, Segment b) {
    return a.x0 < b.x0;
}

int main() {
    int tot_case;
    scanf("%d", &tot_case);
    while (tot_case--) {
        // Input.
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%d%d%d", &seg[i].y1, &seg[i].y2, &seg[i].x0);
            seg[i].y1 *= 2;
            seg[i].y2 *= 2;
        }
        // Solve.
        sort(seg, seg + n, Cmp);
        for (int i = 0; i < n; i++)
            vec[i].clear();
        tree.Build();
        for (int i = 0; i < n; i++) {
            memset(bo, false, sizeof(bo));
            tree.Query(0, 8000 * 2, ROOT, seg[i].y1, seg[i].y2, i, vec, bo);
            tree.Modify(0, 8000 * 2, ROOT, seg[i].y1, seg[i].y2, i);
        }
        int ans = 0;
        for (int i = 0; i < n; i++) {
            int num_seg1= i;
            for (int j = 0; j < vec[num_seg1].size(); j++) {
                int num_seg2 = vec[num_seg1][j];
                for (int k = 0; k < vec[num_seg2].size(); k++)
                    for (int l = 0; l < vec[num_seg1].size(); l++)
                        if (vec[num_seg1][l] == vec[num_seg2][k])
                            ans++;
            }
        }
        // Output.
        printf("%d\n", ans);
    }
    
    return 0;
}



你可能感兴趣的:(算法,vector,struct,tree,query,Build)