[BZOJ 3888] [Usaco2015 Jan] Stampede 【线段树】
题目链接:BZOJ - 3888
题目分析
首先,计算出每个线段在 x 坐标 0 处出现的时间开始点和结束点,就转成了时间轴上的线段。
然后就是看每条线段是否被 y 比它小的线段完全覆盖了。注意求出的时间点要离散化,然后应该使用时间轴上的区间来表示,两线段端点重合并不是有共同部分。
将所有线段按照 y 从小到大排序之后,使用线段树判断它覆盖的区间是否已经都被前面的线段覆盖了。
然后将它所覆盖的区间覆盖。
就这样的一道题我WA了7次,还有救吗..
代码
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> using namespace std; const int MaxN = 100000 + 5; map<int, int> M; int n, Top, Ans, MN; int NA[MaxN], T[MaxN * 4], D[MaxN * 4]; inline int gmin(int a, int b) {return a < b ? a : b;} struct ES { int s, t, Height; } E[MaxN]; inline bool Cmp(ES e1, ES e2) { return e1.Height < e2.Height; } inline void Paint(int x) { T[x] = D[x] = 1; } inline void PushDown(int x) { if (D[x] == 0) return; Paint(x << 1); Paint(x << 1 | 1); D[x] = 0; } int Get(int x, int s, int t, int l, int r) { if (l <= s && r >= t) return T[x]; PushDown(x); int m = (s + t) >> 1, ret = 1; if (l <= m) ret = gmin(ret, Get(x << 1, s, m, l, r)); if (r >= m + 1) ret = gmin(ret, Get(x << 1 | 1, m + 1, t, l, r)); return ret; } inline void Update(int x) { T[x] = gmin(T[x << 1], T[x << 1 | 1]); } void Cover(int x, int s, int t, int l, int r) { if (l <= s && r >= t) { Paint(x); return; } PushDown(x); int m = (s + t) >> 1; if (l <= m) Cover(x << 1, s, m, l, r); if (r >= m + 1) Cover(x << 1 | 1, m + 1, t, l, r); Update(x); } int main() { scanf("%d", &n); int x, y, r; for (int i = 1; i <= n; ++i) { scanf("%d%d%d", &x, &y, &r); E[i].Height = y; E[i].s = (-x - 1) * r; E[i].t = E[i].s + r; NA[++Top] = E[i].s; NA[++Top] = E[i].t; } sort(NA + 1, NA + Top + 1); int p = 0; for (int i = 1; i <= Top; ++i) { if (i != 1 && NA[i] == NA[i - 1]) continue; M[NA[i]] = ++p; } for (int i = 1; i <= n; ++i) { E[i].s = M[E[i].s]; E[i].t = M[E[i].t] - 1; } sort(E + 1, E + n + 1, Cmp); MN = n * 2 + 5; for (int i = 1; i <= n; ++i) { if (Get(1, 1, MN, E[i].s, E[i].t) == 0) ++Ans; Cover(1, 1, MN, E[i].s, E[i].t); } printf("%d\n", Ans); return 0; }