考虑一个暴力的
O(R2C)
O
(
R
2
C
)
算法:
先预处理出:
l[i,j]
l
[
i
,
j
]
:位置
(i,j)
(
i
,
j
)
向左延伸到最多的 1 的个数(不包括
(i,j)
(
i
,
j
)
本身)
r[i,j]
r
[
i
,
j
]
:位置
(i,j)
(
i
,
j
)
向右延伸到最多的 1 的个数(不包括
(i,j)
(
i
,
j
)
本身)
u[i,j]
u
[
i
,
j
]
:位置
(i,j)
(
i
,
j
)
向上延伸到最多的 1 的个数(不包括
(i,j)
(
i
,
j
)
本身)
d[i,j]
d
[
i
,
j
]
:位置
(i,j)
(
i
,
j
)
向下延伸到最多的 1 的个数(不包括
(i,j)
(
i
,
j
)
本身)
并设
lr[i,j]=min(l[i,j],r[i,j])
l
r
[
i
,
j
]
=
min
(
l
[
i
,
j
]
,
r
[
i
,
j
]
)
。
那么先枚举列(记为第
k
k
列),再枚举第 列上的一对合法位置(记为
(i,k)
(
i
,
k
)
和
(j,k)
(
j
,
k
)
) ,并把
(i,k)
(
i
,
k
)
作为双十字的上交点,
(j,k)
(
j
,
k
)
作为双十字的下交点,那么这样分
lr[i,k]≥lr[j,k]
l
r
[
i
,
k
]
≥
l
r
[
j
,
k
]
和
lr[i,k]<lr[j,k]
l
r
[
i
,
k
]
<
l
r
[
j
,
k
]
两种情况处理。
如果
lr[i,k]≥lr[j,k]
l
r
[
i
,
k
]
≥
l
r
[
j
,
k
]
,那么会产生的双十字个数为:
否则会产生的双十字个数为:
考虑使用树状数组来优化。还是一列一列地枚举。
但是在每一列内,需要按照连续的 1 进行分段。
记三个树状数组,在当前枚举到点 (i,j) ( i , j ) 时,存储的是 与 (i,j) ( i , j ) 在同一段内且横坐标严格大于 i+1 i + 1 的所有点。三个树状数组(树状数组的关键字为 lr[x,y] l r [ x , y ] )分别表示:
第一个树状数组:所有合法点 (x,y) ( x , y ) 的 lr[x,y]×(lr[x,y]−1)2×d[x,y] l r [ x , y ] × ( l r [ x , y ] − 1 ) 2 × d [ x , y ] 之和。
第二个树状数组:所有合法点 (x,y) ( x , y ) 的 lr[x,y]×d[x,y] l r [ x , y ] × d [ x , y ] 之和。
第三个树状数组:所有合法点 (x,y) ( x , y ) 的 d[x,y] d [ x , y ] 之和。
注意第一个树状数组维护的是前缀和,第二和第三个树状数组维护的是后缀和。
(以下记 T1(x) T 1 ( x ) 为第一个树状数组以 x x 为结尾的前缀和, 分别为第二个和第三个树状数组以 x x 为结尾的后缀和)
这样,就只需要枚举双十字的上交点 ,
第一种情况,产生的双十字数目为:
第二种情况,产生的双十字数目为:
复杂度 O(R×C×logN) O ( R × C × log N ) 。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
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 = 1e4 + 5, M = 12e5 + 5, PYZ = 1e9 + 9;
int n, m, T, q, _l[M], _r[M], _u[M], _d[M], orz[M], ans,
T1[N + 1375], T2[N + 1375], T3[N + 1375];
bool a[M];
int which(int i, int j) {return (i - 1) * m + j;}
void change1(int x, int v) {
for (; x <= 11370; x += x & -x) T1[x] = (T1[x] + v) % PYZ;
}
int ask1(int x) {
int res = 0; for (; x; x -= x & -x) res = (res + T1[x]) % PYZ; return res;
}
void change2(int x, int v) {
for (; x <= 11370; x += x & -x) T2[x] = (T2[x] + v) % PYZ;
}
int ask2(int x) {
int res = 0; for (; x; x -= x & -x) res = (res + T2[x]) % PYZ; return res;
}
void change3(int x, int v) {
for (; x <= 11370; x += x & -x) T3[x] = (T3[x] + v) % PYZ;
}
int ask3(int x) {
int res = 0; for (; x; x -= x & -x) res = (res + T3[x]) % PYZ; return res;
}
int main() {
int i, j, k, nxt; T = (n = read()) * (m = read()); q = read();
memset(a, true, sizeof(a)); while (q--)
i = read(), j = read(), a[which(i, j)] = 0;
For (i, 1, n) For (j, 1, m) {
int x = which(i, j);
_l[x] = a[x] ? (j == 1 ? 1 : _l[which(i, j - 1)] + 1) : 0;
_u[x] = a[x] ? (i == 1 ? 1 : _u[which(i - 1, j)] + 1) : 0;
}
Rof (i, n, 1) Rof (j, m, 1) {
int x = which(i, j);
_r[x] = a[x] ? (j == m ? 1 : _r[which(i, j + 1)] + 1) : 0;
_d[x] = a[x] ? (i == n ? 1 : _d[which(i + 1, j)] + 1) : 0;
}
For (i, 1, T) orz[i] = min(_l[i], _r[i]) - 1;
For (j, 1, m) for (i = 1; i <= n;) {
if (!a[which(i, j)]) {i++; continue;}
nxt = i; while (nxt <= n && a[which(nxt, j)]) nxt++;
Rof(k, nxt - 2, i) {
int x = which(k, j);
ans = (ans + 1ll * ask1(orz[x]) * (_u[x] - 1) % PYZ) % PYZ;
ans = (ans + 1ll * ask2(10272 - orz[x]) * orz[x] % PYZ
* (_u[x] - 1) % PYZ) % PYZ;
ans = (ans - 1ll * ask3(10272 - orz[x]) *
(1ll * orz[x] * (orz[x] + 1) >> 1 % PYZ) % PYZ
* (_u[x] - 1) % PYZ + PYZ) % PYZ;
int y = which(k + 1, j); if (orz[y]) {
change1(orz[y], (1ll * orz[y] * (orz[y] - 1) >> 1) % PYZ
* (_d[y] - 1) % PYZ);
change2(10273 - orz[y], 1ll * orz[y] * (_d[y] - 1) % PYZ);
change3(10273 - orz[y], _d[y] - 1);
}
}
Rof(k, nxt - 1, i + 1) {
int y = which(k, j); if (orz[y]) {
change1(orz[y], PYZ - (1ll * orz[y] * (orz[y] - 1) >> 1) % PYZ
* (_d[y] - 1) % PYZ);
change2(10273 - orz[y], PYZ - 1ll * orz[y] * (_d[y] - 1) % PYZ);
change3(10273 - orz[y], PYZ - _d[y] + 1);
}
}
i = nxt;
}
cout << ans << endl;
return 0;
}