# Description

Luogu传送门

# Solution

观察到 nn 极小,坐标的范围也不是特别大,我们考虑直接暴力求解!

不难发现,一个三角形覆盖的格子中分为两种情况:

  • 全部覆盖。

  • 覆盖了左下角,右上角没有被覆盖。

因此我们可以把一个格子拆成左下角和右上角两个小三角形来计算,然后总格子数会是一个整数,最后答案再除以 2 即可。

再来看这个小三角形如何统计。

我们考虑枚举每一列,然后开一个数组记录当前列被覆盖的情况。

不难发现由于三角形的摆放方式的特点,第 x+1x + 1 列与第 xx 列是有很多相似之处的,具体来说:

  • 如果一个三角形这两列都不包含自然无事发生

  • 如果一个输入的三角形同时包含了这两列,那么第 xx 列会比第 x+1x + 1 列多包含两个小三角形。

  • 如果第一个三角形包含 x+1x + 1 列,但不包含 xx 列,那么需要对这个数组进行清空。

是不是很简单?

这个复杂度为什么是对的呢?明明看上去是 O(nS2)O(nS^2) 的啊(外层枚举列,对于每一列枚举每个三角形,最内层是上述第三种情况的清空)

但事实上清空操作只会出现 nn 次,所以复杂度还是 O(nS)O(nS) 的。

# 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
#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;
}

# End