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))有多少组特殊的三角形了。
其中有几点要注意的:
- 离散化问题
- 成段更新
// 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;
}
本文介绍了一种使用线段树解决特殊三角形组合问题的方法。通过逆时针旋转线段并利用线段树进行查询与更新,实现了对平行线段间可视性的判断,并最终计算出特定条件下形成的三角形数量。
6万+

被折叠的 条评论
为什么被折叠?



