单调队列 + 组合数统计 Gym 101102D

题目链接: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
*/
View Code
原文地址:https://www.cnblogs.com/heimao5027/p/6738715.html