HDU 5749 Colmerauer(单调栈、思维、组合)

题目链接:
HDU 5749 Colmerauer
题意:
给一个 nm 的矩阵,定义长为 a 宽为b的矩阵的函数 S(a,b) 是矩阵中所有鞍点的权值和。一个矩阵的鞍点是所有满足在同一行中唯一最小,同一列中唯一最大的点。求:

i=1i=nj=1j=mijS(i,j)(mod 232)

数据范围: n,m1000
分析:
首先利用反证法可以得到一个矩阵 最多只有一个鞍点。通过鞍点的定义我们可以借助 单调栈得到矩阵中第 x 行第y列元素最远可以属于哪个子矩阵的鞍点,也就是:在第 x 行中,这个元素在第ya列到 y+b 列中都是唯一最小值; 第 y 列中, 这个元素在第xc x+d 行中都是唯一最大值.
其实我们是想枚举所有元素,然后计算以每个元素作为鞍点的所有子矩阵的 ijS(i,j) 之和,其中 S(i,j) 自然就是这个元素。先考虑所有子矩阵的长求和,因为对于每种长它们所对应的宽都是一样多的。长的端点范围是: [ja[i][j],j+b[i][j]] ,闭区间且是第 j 列。

参考博文 ChallengerRumble

一个位于(x,y)的方格,向左向右向上向下最多分别可以延伸: a,b,c,d 个单位,求包含这个方格的所有子矩阵的面积和。

S=(L+R1)(U+D1)(1La,1Rb,1Uc,1Rd)=1La,1Rb(L+R1)1Uc,1Dd(U+D1)=(L=1L=aLb+R=1R=bRaab)(U=1U=cUd+D=1D=dDccd)=(a(a+1)2b+b(b+1)2aab)(c(c+1)2d+d(d+1)2ccd)=abcd(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值