洛谷P13647 [NOISG 2016] Fabric 题解 笛卡尔树

题目链接: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;
}
posted @ 2026-09-04 20:32  quanjun  阅读(10)  评论(0)    收藏  举报