[2020.11.14提高组模拟] 鬼渊传说(village)
GMOJ 6862.鬼渊传说
一道由不会证的结论 + 十分繁琐 \(code\) 组成的题,改了一个晚上 + 半个早上......
差评!
\(Solution\)
首先由 欧拉公式 (%%%LZA):\(\text{n + r - m = 2}\),其中 \(n\) 为点数,\(r\) 为面数(包括整个图之外的面), \(m\) 为边数。
若不存在空腔,则 \(r =\) 四元环个数 \(+ 1\),(将四元环看成一个面),综上得:\(n +\) 四元环个数 \(- m = 1\)。
那么不考虑空腔的话,可以枚举 \(O(n^3)\) 上下右边界,将 \(n\)、\(m\)、四元环个数分别前缀和预处理好,用一个桶 \(O(1)\) 查询合法左边界个数即可。前缀和时需注意横竖边,为避免繁琐细节可以分为横、竖边分别前缀和,但是依旧很繁琐好吗。
若一个矩形包含空腔,那么其向任意方向扩展后也依旧包含空腔,注意空腔个数上限 \(n^2\),所以可以预处理出所有空腔,对于空腔所在的最小的子矩形 \((x1, y1, x2, y2)\),若上界 \(i < x1\),那么在 \((x2 + 1, y2 + 1)\) 打上 \(y1 - 1\) 的限制,同样 \(O(n^2)\),最后记得下传(\((x, y) \rightarrow (x + 1, y)\)),指针 + 桶维护即可。
时间复杂度 \(O(n ^ 3)\)。
\(Code\)
丑的心态很爆炸
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 301
#define M 90001
#define inf 1000000
#define fo(i, x, y) for(int i = x; i <= y; i ++)
struct Arr {
int x, y;
bool operator == (const Arr& a) {
return x == a.x && y == a.y;
}
};
struct Mat {
int x1, y1, x2, y2, fx, fy;
void Insert(int x, int y) { x1 = x2 = fx = x, y1 = y2 = fy = y; }
Arr operator = (const Arr& a) {
return (Arr) { fx = a.x, fy = a.y };
}
void operator += (const Mat &a) {
x1 = min(x1, a.x1), y1 = min(y1, a.y1);
x2 = max(x2, a.x2), y2 = max(y2, a.y2);
}
} p[N + 1][N + 1];
struct Mat1 {
int x1, y1, x2, y2;
void operator = (const Mat& a) {
x1 = a.x1, y1 = a.y1, x2 = a.x2, y2 = a.y2;
}
} c[M + 1];
char ch[N + 1][N + 1];
int fn[N + 1][N + 1], fm[N + 1][N + 1], fr[N + 1][N + 1], a[N + 1][N + 1], gm[N + 1][N + 1];
int n, m, tot = 0;
Arr Getf(int x, int y) { return (x == p[x][y].fx && y == p[x][y].fy) ? (Arr) { x, y } : p[x][y] = Getf(p[x][y].fx, p[x][y].fy); }
void Merge(int x1, int y1, int x2, int y2) {
Arr u = Getf(x1, y1), v = Getf(x2, y2);
if (u == v) return;
p[u.x][u.y] += p[v.x][v.y];
p[v.x][v.y].fx = u.x, p[v.x][v.y].fy = u.y;
}
void Init() {
fo(i, 1, n) fo(j, 1, m) a[i][j] = ch[i][j] - 48;
fo(i, 1, n) fo(j, 1, m)
fn[i][j] = fn[i - 1][j] + fn[i][j - 1] - fn[i - 1][j - 1] + a[i][j];
fo(i, 1, n) fo(j, 1, m)
fm[i][j] = fm[i - 1][j] + fm[i][j - 1] - fm[i - 1][j - 1] + (a[i][j] & a[i][j - 1]);
fo(i, 1, n) fo(j, 1, m)
fr[i][j] = fr[i - 1][j] + fr[i][j - 1] - fr[i - 1][j - 1] + (a[i][j] & a[i - 1][j] & a[i][j - 1] & a[i - 1][j - 1]);
fo(i, 1, n) fo(j, 1, m)
gm[i][j] = gm[i - 1][j] + gm[i][j - 1] - gm[i - 1][j - 1] + (a[i][j] & a[i - 1][j]);
fo(i, 1, n) fo(j, 1, m) if (! a[i][j]) {
p[i][j].Insert(i, j);
if (j > 1 && ! a[i][j - 1])
Merge(i, j - 1, i, j);
if (i > 1 && ! a[i - 1][j])
Merge(i - 1, j, i, j);
}
fo(i, 1, n) fo(j, 1, m)
if ((Arr) { i, j } == Getf(i, j))
c[ ++ tot ] = p[i][j];
}
int s[N + 1][N + 1], h[inf << 5];
#define f1(j, k) (fn[j][k] + fr[j][k + 1] - fm[j][k + 1] - gm[j][k])
#define f2(i, k) (fn[i][k] + fr[i + 1][k] - gm[i + 1][k] - fm[i][k])
#define f3(i, j) (fn[i - 1][j - 1] + fr[i][j] - (fm[i - 1][j] + gm[i][j - 1]))
#define f(i, j) (fn[i][j] + fr[i][j] - fm[i][j] - gm[i][j])
int main() {
freopen("village.in", "r", stdin);
freopen("village.out", "w", stdout);
scanf("%d %d\n", &n, &m);
fo(i, 1, n) scanf("%s\n", ch[i] + 1);
Init();
int ans = 0;
fo(i, 1, n) {
fo(j, 0, n) fo(k, 0, m) s[j][k] = -1;
fo(j, 1, tot) if (i < c[j].x1)
s[c[j].x2 + 1][c[j].y2 + 1] = max(c[j].y1 - 1, s[c[j].x2 + 1][c[j].y2 + 1]);
fo(k, 1, m) fo(j, 1, n) s[j][k] = max(s[j][k], s[j - 1][k]);
fo(j, i, n) {
int l = 0;
fo(k, 0, m) {
if (k) {
while (l < s[j][k] && l <= k)
-- h[f3(i, l + 1) - f1(j, l) + inf], ++ l;
ans += h[1 - (f(j, k) - f2(i - 1, k)) + inf];
}
++ h[f3(i, k + 1) - f1(j, k) + inf];
}
while (l <= m)
-- h[f3(i, l + 1) - f1(j, l) + inf], ++ l;
}
}
printf("%d\n", ans);
return 0;
}