洛谷P13647 [NOISG 2016] Fabric 题解 笛卡尔树
- 双倍经验:abc420_f. kirinuki(求面积 \(\le K\) 的全 '.' 子矩阵个数)
题目链接:https://www.luogu.com.cn/problem/P13647
题目大意:求面积 \(\ge K\) 的全 \(0\) 子矩阵个数。
解题思路参考自 P2441M 大佬的博客。
考虑 \(h \times w\)(高为 \(h\),宽度为 \(w\))的全 \(0\) 矩形中面积 \(\ge K\) 的子矩形数量为 \(f_{h, w}\)
考虑先计算 \(h \times w\) 的全 \(0\) 矩形中所有满足“高度恰好为 \(h\),面积 \(\ge K\)”的子矩形数量 \(cnt_{h, w}\):
取 \(min_w = \lceil \frac{K}{h} \rceil\)
- 若 \(min_w \ge w\),则 \(cnt_{h, w} = 0\)
- 否则,\(cnt_{h, w} = 1 + 2 + \ldots + (w - min_w + 1) = \frac{ (w - min_w + 2) (w - mind_w + 1) }{2}\)
而 \(f_{h, w}\) 遵循以下递推式:
\[f_{h, w} = f_{h-1, w} + cnt_{h, w}
\]
我们可以 \(O(n, m)\) 预处理出所有 \(f_{h, w}\),然后按照上述题解的思路基于 笛卡尔树 求解该问题。
示例程序:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 2005;
int n, m, K, a[maxn], ls[maxn], rs[maxn], rt;
ll f[maxn][maxn], ans;
void init() {
for (int h = (K - 1) / m + 1; h <= n; h++) {
int min_w = (K - 1) / h + 1;
for (int w = min_w; w <= m; w++) {
f[h][w] = f[h-1][w] + (w - min_w + 2) * (w - min_w + 1) / 2;
}
}
}
void dfs(int u, int p, int l, int r) {
if (!u) return;
ans += f[ a[u] ][ r-l+1 ] - f[ a[p] ][ r-l+1 ];
dfs(ls[u], u, l, u-1);
dfs(rs[u], u, u+1, r);
}
void solve() {
fill(ls+1, ls+m+1, 0);
fill(rs+1, rs+m+1, 0);
stack<int> stk;
for (int i = 1; i <= m; i++) {
int last = 0;
while (!stk.empty() && a[stk.top()] > a[i]) {
last = stk.top();
stk.pop();
}
if (!stk.empty()) rs[stk.top()] = i;
else rt = i;
ls[i] = last;
stk.push(i);
}
dfs(rt, 0, 1, m);
}
int main() {
scanf("%d%d%d", &n, &m, &K);
init();
for (int i = 1; i <= n; i++) {
for (int j = 1, x; j <= m; j++) {
scanf("%d", &x);
if (x) a[j] = 0;
else a[j]++;
}
solve();
}
printf("%lld\n", ans);
return 0;
}
浙公网安备 33010602011771号