# Description
Luogu 传送门
# Solution
观察到 极小,坐标的范围也不是特别大,我们考虑直接暴力求解!
不难发现,一个三角形覆盖的格子中分为两种情况:
全部覆盖。
覆盖了左下角,右上角没有被覆盖。
因此我们可以把一个格子拆成左下角和右上角两个小三角形来计算,然后总格子数会是一个整数,最后答案再除以 2 即可。
再来看这个小三角形如何统计。
我们考虑枚举每一列,然后开一个数组记录当前列被覆盖的情况。
不难发现由于三角形的摆放方式的特点,第 列与第 列是有很多相似之处的,具体来说:
如果一个三角形这两列都不包含自然无事发生
如果一个输入的三角形同时包含了这两列,那么第 列会比第 列多包含两个小三角形。
如果第一个三角形包含 列,但不包含 列,那么需要对这个数组进行清空。
是不是很简单?
这个复杂度为什么是对的呢?明明看上去是 的啊(外层枚举列,对于每一列枚举每个三角形,最内层是上述第三种情况的清空)
但事实上清空操作只会出现 次,所以复杂度还是 的。
# Code
#include <bits/stdc++.h> | |
#define ll long long | |
using namespace std; | |
namespace IO{ | |
inline int read(){ | |
int x = 0, f = 1; | |
char ch = getchar(); | |
while(!isdigit(ch)){if(ch == '-') f = -1; ch = getchar();} | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); | |
return x * f; | |
} | |
template <typename T> inline void write(T x){ | |
if(x > 9) write(x / 10); | |
putchar(x % 10 + '0'); | |
} | |
} | |
using namespace IO; | |
const int N = 4e6 + 10; | |
int n, mx; | |
struct node{ | |
int x, y, r, right; | |
}a[15]; | |
bool b[N]; | |
ll cnt, ans; | |
inline void update(int x) {cnt += (b[x] ? -1 : 1), b[x] ^= 1;} | |
int main(){ | |
n = read(); | |
for(int i = 1; i <= n; ++i){ | |
a[i] = (node){read(), read(), read()}; | |
a[i].right = a[i].y + a[i].r, mx = max(mx, a[i].right);//right 是三角形右下角的横坐标 | |
} | |
for(int i = mx - 1; i >= 1; --i){// 枚举每一列 | |
for(int j = 1; j <= n; ++j){ | |
if(i >= a[j].y && i < a[j].right){// 当前列和右边一列都在这个三角形中 | |
int tmp = (a[j].right - i + a[j].x) << 1;// 手玩一下发现是多出来的一个小三角形 | |
update(tmp); | |
if(i != a[j].right - 1) update(tmp - 1);// 同样手玩一下,是多出来的另一个小三角形,但是这个要特判一下,应该不难理解吧 | |
} | |
if(i == a[j].y - 1){// 清空 | |
int tmp = a[j].right - (i + 1) + a[j].x; | |
for(int k = (a[j].x + 1) << 1; k <= (tmp << 1); ++k) update(k); | |
} | |
} | |
ans += cnt; | |
} | |
printf("%.1f\n", 1.0 * ans / 2); | |
return 0; | |
} |