CodeforcesGym - 101102D. Rectangles 单调栈+dp优化

1.     题目描述:

Given an R×C gridwith each cell containing an integer, find the number of subrectangles in thisgrid that contain only one distinct integer; this means every cell in asubrectangle contains the same integer.

A subrectangle isdefined by two cells: the top left cell (r1, c1),and the bottom-right cell (r2, c2) (1 ≤ r1 ≤ r2 ≤ R) (1 ≤ c1 ≤ c2 ≤ C),assuming that rows are numbered from top to bottom and columns are numberedfrom left to right.

Input

The first line ofinput contains a single integer T, the number of test cases.

The first line ofeach test case contains two integers R and C (1 ≤ R, C ≤ 1000),the number of rows and the number of columns of the grid, respectively.

Each of thenext R lines contains C integers between 1 and 109,representing the values in the row.

Output

For each testcase, print the answer on a single line.

Example

Input

1
3 3
3 3 1
3 3 1
2 2 5

Output

16

2.     题意概述:

给你一个RxC矩阵,里面每一个矩阵元素中有一个数在1~1e9之间,求有多少个子矩阵里面的所有数相同?

3.     题解思路:

先对每一列从上到下遍历,用h[i][j]记录从当前元素往上最多有多少数相同, 然后对每一行遍历,用ans[i][j] 记录以a[i][j]为右下角的元素的子矩阵个数,那么我们可以发现:设当前元素之前同一行中小于它且最近的元素为a[i][k] 那么ans[i][j]=ans[i][k]+(j-k)*h[i][j]为了降低复杂度可以在当前行从左向右遍历的过程中把各列的高放入单调栈中,这样可以快速知道小于当前元素最近的元素的位置,最后对ans[][]求和就是结果 ;

4.     Ac代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <functional>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <ctime>
#define INF 0x7fffffff
#define maxn 1111
#define eps 1e-6
#define pi acos(-1.0)
#define e 2.718281828459
#define mod (int)1e9 + 7;
using namespace std;
typedef long long ll;
int a[maxn][maxn], h[maxn][maxn], p[maxn][maxn], s[maxn * maxn];
int main()
{
#ifndef ONLINE_JUDGE
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
  long _begin_time = clock();
#endif
  int r, c, t;
  scanf("%d", &t);
  while (t--)
  {
    memset(h, 0, sizeof(h));
    memset(s, 0, sizeof(s));
    memset(p, 0, sizeof(p));
    scanf("%d%d", &r, &c);
    for (int i = 1; i <= r; i++)
      for (int j = 1; j <= c; j++)
      {
        scanf("%d", &a[i][j]);
        h[i][j] = a[i][j] == a[i - 1][j] ? h[i - 1][j] + 1 : 1;
      }
    int cnt =1;
    ll ans = 0;
    for (int i = 1; i <= r; i++)
      for (int j = 1; j <= c; j++)
      {
        if (a[i][j - 1] != a[i][j]) //不等则求之
        {
          cnt = 1;
          s[1] = j - 1; // 单调栈初始化
          h[i][j - 1] = 0;  //无法延伸
          s[++cnt] = j;
          p[i][j] = h[i][j];
          ans += 1LL * p[i][j]; // 防爆
        }
        else // 等则并之
        {
          while (h[i][j] <= h[i][s[cnt]]) // 单调栈求比他小的第一个位置,顺便把大的元素全部出栈
            cnt--;
          int pos = s[cnt];               // 记录下来
          s[++cnt] = j;                   //入栈
          p[i][j] += h[i][j] * (j - pos); // 求出来记录之
          if (h[i][pos] != 0)
            p[i][j] += p[i][pos];
          ans += 1LL * p[i][j];
        }
      }
    printf("%lld\n", ans);
  }

#ifndef ONLINE_JUDGE
  long _end_time = clock();
  printf("time = %ld ms\n", _end_time - _begin_time);
#endif
  return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值