下面定义
(i,j)
(
i
,
j
)
表示第
i
i
行的第个顶点(共
n+1
n
+
1
行,第
i
i
行有个顶点)
考虑一个朴素的做法:
一、利用递推,预处理出
5
5
个值:
(1):
(i,j)
(
i
,
j
)
通过未被删除的边,向左延伸的最长距离,如样例中
lef[3][3]=0
l
e
f
[
3
]
[
3
]
=
0
,
lef[4][4]=3
l
e
f
[
4
]
[
4
]
=
3
。
(2)
leup[i][j]
l
e
u
p
[
i
]
[
j
]
:
(i,j)
(
i
,
j
)
通过未被删除的边,向左上延伸的最长距离。
(3)
riup[i][j]
r
i
u
p
[
i
]
[
j
]
:
(i,j)
(
i
,
j
)
通过未被删除的边,向右上延伸的最长距离。
(4)
ledw[i][j]
l
e
d
w
[
i
]
[
j
]
:
(i,j)
(
i
,
j
)
通过未被删除的边,向左下延伸的最长距离。
(5)
ridw[i][j]
r
i
d
w
[
i
]
[
j
]
:
(i,j)
(
i
,
j
)
通过未被删除的边,向右下延伸的最长距离。
统计正立三角形时,先枚举右下顶点
(i,j)
(
i
,
j
)
。容易得出,
右下顶点为
(i,j)
(
i
,
j
)
的正立三角形个数不超过
min(lef[i][j],leup[i][j])
min
(
l
e
f
[
i
]
[
j
]
,
l
e
u
p
[
i
]
[
j
]
)
。
记
k=j−min(lef[i][j],leup[i][j])
k
=
j
−
min
(
l
e
f
[
i
]
[
j
]
,
l
e
u
p
[
i
]
[
j
]
)
,那么第
i
i
行的第个顶点都有可能成为正立三角形的左下角。
而对于任何的
h∈[k,j−1]
h
∈
[
k
,
j
−
1
]
,
(i,h)
(
i
,
h
)
向右上延伸后,能与
(i,j)
(
i
,
j
)
构成正立三角形的充分必要条件是:
统计倒立三角形也一样,先枚举右上顶点 (i,j) ( i , j ) ,
右上顶点为 (i,j) ( i , j ) 的正立三角形个数不超过 min(lef[i][j],ledw[i][j]) min ( l e f [ i ] [ j ] , l e d w [ i ] [ j ] ) 。
记 k=j−min(lef[i][j],ledw[i][j]) k = j − min ( l e f [ i ] [ j ] , l e d w [ i ] [ j ] ) ,那么第 i i 行的第个顶点都有可能成为倒立三角形的左上角。
对于任何的 h∈[k,j−1] h ∈ [ k , j − 1 ] , (i,h) ( i , h ) 向右下延伸后,能与 (i,j) ( i , j ) 构成倒立三角形的充分必要条件是:
复杂度是 O(n3) O ( n 3 ) 的。考虑将上面给出的两个不等式移项:
这样就转换成了求一个区间内,有多少个数大于一个定值。将询问离线排序之后,可以使用树状数组解决。复杂度 O(n2logn) O ( n 2 log n ) 。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 1024, M = 4096;
int n, led[N][N], rid[N][N], lef[N][N], leu[N][N], riu[N][N], T[M], tot; ll ans;
void change(int x, int v) {
for (; x <= 4000; x += x & -x) T[x] += v;
}
int ask(int x) {
int res = 0; for (; x; x -= x & -x) res += T[x]; return res;
}
struct cyx {
int id, ri;
cyx() {}
cyx(int _id, int _ri) : id(_id), ri(_ri) {}
} otz[M];
inline bool comp(const cyx &a, const cyx &b) {
return a.ri > b.ri;
}
int main() {
int i, j, x, y, z; n = read();
for (i = 1; i <= n; i++) for (j = 1; j <= i; j++) {
x = read(); y = read(); z = read();
lef[i + 1][j + 1] = z; led[i][j] = riu[i + 1][j] = x;
rid[i][j] = leu[i + 1][j + 1] = y;
}
n++; for (i = 1; i <= n; i++) for (j = 1; j <= i; j++)
lef[i][j] = lef[i][j] ? lef[i][j - 1] + 1 : 0;
for (j = 1; j <= n; j++) for (i = j; i <= n; i++) {
leu[i][j] = leu[i][j] ? leu[i - 1][j - 1] + 1 : 0;
riu[i][j] = riu[i][j] ? riu[i - 1][j] + 1 : 0;
}
for (i = n; i; i--) for (j = 1; j <= i; j++) {
led[i][j] = led[i][j] ? led[i + 1][j] + 1 : 0;
rid[i][j] = rid[i][j] ? rid[i + 1][j + 1] + 1 : 0;
}
for (i = 1; i <= n; i++) for (j = 1; j <= i; j++)
riu[i][j] += j, rid[i][j] += j;
for (i = 1; i <= n; i++) {
for (j = 0; j <= 4000; j++) T[j] = 0; tot = 0;
for (j = 1; j <= i; j++) otz[++tot] = cyx(j, riu[i][j]);
sort(otz + 1, otz + tot + 1, comp); int orz = 1;
for (j = i; j; j--) {
while (orz <= tot && otz[orz].ri >= j)
change(otz[orz].id, 1), orz++;
ans += ask(j - 1) - ask(j - min(lef[i][j], leu[i][j]) - 1);
}
for (j = 0; j <= 4000; j++) T[j] = 0; tot = 0;
for (j = 1; j <= i; j++) otz[++tot] = cyx(j, rid[i][j]);
sort(otz + 1, otz + tot + 1, comp); orz = 1;
for (j = i; j; j--) {
while (orz <= tot && otz[orz].ri >= j)
change(otz[orz].id, 1), orz++;
ans += ask(j - 1) - ask(j - min(lef[i][j], led[i][j]) - 1);
}
}
cout << ans << endl;
return 0;
}