题目链接:
HDU 5749 Colmerauer
题意:
给一个
n∗m
的矩阵,定义长为
a
宽为
∑i=1i=n∑j=1j=mi∗j∗S(i,j)(mod 232)
数据范围: n,m≤1000
分析:
首先利用反证法可以得到一个矩阵 最多只有一个鞍点。通过鞍点的定义我们可以借助 单调栈得到矩阵中第 x 行第
其实我们是想枚举所有元素,然后计算以每个元素作为鞍点的所有子矩阵的 i∗j∗S(i,j) 之和,其中 S(i,j) 自然就是这个元素。先考虑所有子矩阵的长求和,因为对于每种长它们所对应的宽都是一样多的。长的端点范围是: [j−a[i][j],j+b[i][j]] ,闭区间且是第 j 列。
一个位于
(x,y) 的方格,向左向右向上向下最多分别可以延伸: a,b,c,d 个单位,求包含这个方格的所有子矩阵的面积和。
S=∑(L+R−1)∗(U+D−1)(1≤L≤a,1≤R≤b,1≤U≤c,1≤R≤d)=∑1≤L≤a,1≤R≤b(L+R−1)∗∑1≤U≤c,1≤D≤d(U+D−1)=(∑L=1L=aL∗b+∑R=1R=bR∗a−a∗b)∗(∑U=1U=cU∗d+∑D=1D=dD∗c−c∗d)=(a∗(a+1)2∗b+b∗(b+1)2∗a−a∗b)∗(c∗(c+1)2∗d+d∗(d+1)2∗c−c∗d)=a∗b∗c∗d∗(a+b)∗(c+d)4因为是对 232 取模,用unsigned int 可以避免取模。
#include <stdio.h> #include <string.h> #include <algorithm> #include <math.h> #include <iostream> #include <climits> using namespace std; typedef long long ll; typedef unsigned int uint; const int MAX_N = 1010; const ll mod = (ll)1 << 32; const int inf = 0x3f3f3f3f; int T, n, m; int mat[MAX_N][MAX_N]; int a[MAX_N][MAX_N], b[MAX_N][MAX_N], c[MAX_N][MAX_N], d[MAX_N][MAX_N], sta[MAX_N]; //行:单调递增;列:单调递减 void SolveRow(int row) { int top = 0, cur; for (int j = 1; j <= m; ++j) { while (top) { cur = sta[top]; if (mat[row][cur] <= mat[row][j]) break; --top; } if (top == 0) a[row][j] = 1; else a[row][j] = sta[top] + 1; sta[++top] = j; } top = 0; for (int j = m; j >= 1; --j) { while (top) { cur = sta[top]; if (mat[row][cur] <= mat[row][j]) break; --top; } if (top == 0) b[row][j] = m; else b[row][j] = sta[top] - 1; sta[++top] = j; } } void SolveCol(int col) { int top = 0, cur; for (int i = 1; i <= n; ++i) { while (top) { cur = sta[top]; if (mat[cur][col] >= mat[i][col]) break; --top; } if (top == 0) c[i][col] = 1; else c[i][col] = sta[top] + 1; sta[++top] = i; } top = 0; for (int i = n; i >= 1; --i) { while (top) { cur = sta[top]; if (mat[cur][col] >= mat[i][col]) break; --top; } if (top == 0) d[i][col] = n; else d[i][col] = sta[top] - 1; sta[++top] = i; } } uint work(int left, int x, int right) { uint d1 = right - x + 1; uint d2 = x - left + 1; uint res = d1 * (d1 + 1) / 2 * d2 + d2 * (d2 - 1) / 2 * d1; return res; } int main() { scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { scanf("%d", &mat[i][j]); } } for(int i = 1; i <= n; ++i) { SolveRow(i); } for(int j = 1; j <= m; ++j) { SolveCol(j); } uint ans = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { uint tmp1 = work(a[i][j], j, b[i][j]); uint tmp2 = work(c[i][j], i, d[i][j]); //printf("(%d, %d):%lld %lld\n", i, j, tmp1, tmp2); //printf("a: %d b: %d c: %d d: %d\n", a[i][j], b[i][j], c[i][j], d[i][j]); ans += (uint)mat[i][j] * tmp1 * tmp2; } } printf("%u\n", ans); } return 0; }