【题解】21 ZR联赛集训 day4 下象棋

题目链接(正睿)

题目大意

给出\(n\times n~(1\le n \le 10^6)\)棋盘上的一些棋子,问有多少点不与任何一个棋子形成斜率为\(\pm 1\)的斜线。

分析

一个棋子就对应了斜率为\(\pm 1\)的两条斜线,其中每条斜率为\(1\)的线上点\((x,y)\)\(x+y\)相等的性质;斜率为\(-1\)的点有\(x-y\)相等的性质。

这样的线一共有\(4\times n\)条,我们可以遍历每一个棋子,标记它们所在的线。我们还很容易可以算出,每条线上点的数目,即不合法的点。

最后,有可能存在斜线相交的情况,此时交点被算了两次。因为斜率为$ -1$ 的线能交到的斜率为 $1 $的线是⼀个连续的区间,考虑⽤前缀和处理。注意:相交的点如果不是整点,不需要统计。

实现

统计相交线的数量要隔一条统计一次:

1
2
3
4
5
6
7
8
9
10
for (int i = -n + 1; i <= n - 1; i++) {
if ((i + N) % 2 == 0) {
sum[i + N][0] = sum[i + N - 1][0] + booka[i + N];
sum[i + N][1] = sum[i + N - 1][1];
}
else {
sum[i + N][0] = sum[i + N - 1][0];
sum[i + N][1] = sum[i + N - 1][1] + booka[i + N];
}
}

完整代码:

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
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1000006;
bool booka[(N << 1) + 10], bookb[(N << 1) + 10];
ll sum[(N << 1) + 10][2];
ll n, m, ans, x, y, cnta, cntb;

int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> x >> y;
ll a = x - y + N, b = x + y, s, t;
if (!booka[a] && a != n - 1 + N && a != 1-n + N) {
booka[a] = true;
ll p = a - N;
if (p >= 0) s = p + 1, t = n;
else s = 1, t = p + n;
ans += t - s + 1;
}
if (!bookb[b] && b != 2 && b != 2 * n) {
bookb[b] = true;
if (b >= n + 1) s = b - n, t = n;
else s = 1, t = b - 1;
ans += t - s + 1;
}
}
for (int i = -n + 1; i <= n - 1; i++) {
if ((i + N) % 2 == 0) {
sum[i + N][0] = sum[i + N - 1][0] + booka[i + N];
sum[i + N][1] = sum[i + N - 1][1];
}
else {
sum[i + N][0] = sum[i + N - 1][0];
sum[i + N][1] = sum[i + N - 1][1] + booka[i + N];
}
}

for (int i = 2; i <= 2 * n; i++) {
if(!bookb[i]) continue;
ll s, t;
if (i >= n + 1) s = i - 2 * n, t = 2 * n - i;
else s = 2 - i, t = i - 2;
ans -= sum[t + N][i % 2] - sum[s + N - 1][i % 2];
}

ans = n * n - ans;
cout << ans << endl;
return 0;
}