# Description
Luogu传送门
# Solution
观察到 极小,坐标的范围也不是特别大,我们考虑直接暴力求解!
不难发现,一个三角形覆盖的格子中分为两种情况:
-
全部覆盖。
-
覆盖了左下角,右上角没有被覆盖。
因此我们可以把一个格子拆成左下角和右上角两个小三角形来计算,然后总格子数会是一个整数,最后答案再除以 2 即可。
再来看这个小三角形如何统计。
我们考虑枚举每一列,然后开一个数组记录当前列被覆盖的情况。
不难发现由于三角形的摆放方式的特点,第 列与第 列是有很多相似之处的,具体来说:
-
如果一个三角形这两列都不包含自然无事发生
-
如果一个输入的三角形同时包含了这两列,那么第 列会比第 列多包含两个小三角形。
-
如果第一个三角形包含 列,但不包含 列,那么需要对这个数组进行清空。
是不是很简单?
这个复杂度为什么是对的呢?明明看上去是 的啊(外层枚举列,对于每一列枚举每个三角形,最内层是上述第三种情况的清空)
但事实上清空操作只会出现 次,所以复杂度还是 的。
# Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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;
}