题目链接:http://codeforces.com/gym/101102/problem/D
题目大意:给你一个n*m的矩阵,矩阵里面的数值范围为[1,1e9]。这个矩阵有一个值,如果相邻的多个数字是一样的,那么val += 他们的组合数,例如 1 1 1,那么就是val就是6.
问最后这个矩阵是val是多少?
思路:
我们固定右端点,先统计出上方有几个和他数值相同的数字,并把这个定义成这个位置的高,然后每次往统计以这个点为右下角的端点数即可。然后暴力右下角就是O(n*n)
我们发现,如果要每次暴力相同的数字,那么要知道前面有几个数字的高度,然后O(n)的暴力一遍是可以的,但是这样会TLE
所以我们知道,如果高度是>=目前的h[i][j]的高度的,那么我们就ans[i][j] += h[i][j] * (j - k +1),其中k为高度<h[i][j]出现的位置。(这个东西用单调队列或者单调栈维护一下就好了)
若是出现a[i][j] == a[i][k - 1],那么我们就只需要ans[i][j] += ans[i][k - 1]即可
//看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> using namespace std; #pragma comment(linker,"/STACK:102400000,102400000") #define LL long long #define ALL(a) a.begin(), a.end() #define pb push_back #define mk make_pair #define fi first #define se second #define haha printf("haha ") const int maxn = 1000 + 5; int n, m; int h[maxn][maxn], a[maxn][maxn], cnt[maxn][maxn]; int main(){ int t; cin >> t; while (t--){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++){ for (int j = 1; j <= m; j++){ scanf("%d", &a[i][j]); h[i][j] = 1; if (a[i - 1][j] == a[i][j]) h[i][j] += h[i-1][j]; cnt[i][j] = 0; } } LL res = 0; for (int i = 1; i <= n; i++){ deque<pair<int, int> > que; for (int j = 1; j <= m; j++){ if (a[i][j] != a[i][j - 1]){ while (!que.empty()) que.pop_back(); res += h[i][j]; cnt[i][j] = h[i][j]; que.push_back(mk(h[i][j], j)); } else { pair<int, int> p = mk(h[i][j], j); while (!que.empty()){ if (que.back().fi >= p.fi){ p.se = que.back().se; que.pop_back(); } else break; } cnt[i][j] = h[i][j] * (j - p.se + 1); if (a[i][j] == a[i][p.se - 1]) cnt[i][j] += cnt[i][p.se - 1]; res += cnt[i][j]; que.push_back(p); } } } printf("%lld ", res); } return 0; } /* 549 3 3 1 2 1 1 1 1 1 1 1 ans = 25 4564 3 2 1 2 1 1 1 1 ans = 13 45609 3 2 2 1 1 1 1 1 ans = 13 */