说真的这题的做法很迷啊,大部分做法都是 \(O(n^2)\) 级别过的。
我也不例外
不过 \(O(n ^ 2)\) 的做法确实很容易懂。
考虑扫描线,我们把所有三角形按底边的 \(y\) 值排序,然后从下往上扫,每次只移动一个单位长度。
然后我们在这个狭长的矩形里面截出来的面积就一定是若干梯形的面积之和,那么我们只要知道扫描线移动前后的有效长度,也就是上底下底,就可以算出该部分的贡献。
然后考虑怎么加入或删除一个三角形的贡献:考虑到一个三角形只会加入和删除一次,用双向链表,每次把当前扫到的三角形加进来,往上移动扫描线的时候把当前链表中的三角形都砍掉最下面一行,判一下该三角形还存不存在,不存在了就删掉。
加入和删除的过程维护一下每个横坐标被覆盖的次数,然后更新扫描线长度即可。
参考代码:
#include <algorithm>
#include <cstdio>
using namespace std;
template < class T > void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
const int _ = 1e4 + 5, __ = 1e6 + 5;
int n, mx, vis[__], hd, tl, pre[_], nxt[_]; struct node { int x, y, d, l, r; } t[_];
int cmp(node a, node b) { return a.y < b.y; }
void del(int x) { pre[nxt[x]] = pre[x], nxt[pre[x]] = nxt[x]; }
int check(int u) {
if (!t[u].d) return 0;
for (int i = nxt[hd]; i != tl; i = nxt[i])
if (t[i].l <= t[u].l && t[u].r <= t[i].r) return 0;
pre[nxt[hd]] = u, nxt[u] = nxt[hd], nxt[hd] = u, pre[u] = hd;
return 1;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
#endif
read(n);
for (int x, y, d, i = 1; i <= n; ++i)
read(x), read(y), read(d), t[i] = (node) { x, y, d, x, x + d - 1 }, mx = max(mx, y + d);
sort(t + 1, t + n + 1, cmp);
hd = 0, tl = n + 1, nxt[hd] = tl, pre[tl] = hd;
int now = 0, last = 0, ans = 0;
for (int i = t[1].y, j = 1; i <= mx; ++i) {
now = last;
for (int x = nxt[hd]; x != tl; x = nxt[x]) {
if (!--vis[t[x].r]) --now;
if ((--t[x].r) < t[x].l) del(x);
}
ans += now + last;
for (; j <= n && t[j].y == i; ++j)
if (check(j))
for (int x = t[j].l; x <= t[j].r; ++x)
if (!vis[x]++) ++now;
last = now;
}
printf("%.1lf\n", ans / 2.0);
return 0;
}