本文主要是介绍【HDU5749 BestCoder Round 84C】【ST-RMQ?NO!暴力跳法or单调栈法 + 贡献思维】Colmerauer 所有子矩阵size乘鞍点权值和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Colmerauer
Peter有一个n×m的矩阵M. 定义S(a,b)为M的所有大小为a×b的子矩阵的权值和. 一个矩阵的权值是这个矩阵所有鞍点的值的和. 在矩阵中, 一个数在所在行中是唯一的最小值, 在所在列中是唯一的最大值, 则被称为鞍点. 帮助Peter找出所有S(a,b)的值.注意: 本题中鞍点的定义可能和你所知的有所不同.
输入包含多组数据, 第一行包含一个整数T表示测试数据组数. 对于每组数据:第一行包含两个整数n和m (1≤n,m≤1000)表示矩阵的大小.接下来n行每行包含m个非负整数, 表示矩阵M的每一行 (M中每个元素都不超过106).
对于每组数据, 输出一个整数W=(a=1∑nb=1∑ma⋅b⋅S(a,b)) mod 232.
3 2 2 1 1 1 1 3 3 1 2 3 4 5 6 7 8 9 3 3 1 2 1 2 3 1 4 5 2
4 600 215
【HDU5749 BestCoder Round 84C】【ST-RMQ?NO!暴力跳法 + 贡献思维】Colmerauer 所有子矩阵size乘鞍点权值和
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1010, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
UI a[N][N];
UI c[N + N];
UI d[N][N];
int lft[N][N];
int rgt[N][N];
int top[N][N];
int bot[N][N];
void RMQinit()
{for (int i = 1; i <= n; ++i){for (int j = 1; j <= m; ++j){if (j == 1)lft[i][j] = 1;else{int p = j - 1;while (p >= 1 && a[i][p] > a[i][j])p = lft[i][p] - 1;lft[i][j] = p + 1;}if (i == 1)top[i][j] = 1;else{int p = i - 1;while (p >= 1 && a[p][j] < a[i][j])p = top[p][j] - 1;top[i][j] = p + 1;}}}for (int i = n; i >= 1; --i){for (int j = m; j >= 1; --j){if (j == m)rgt[i][j] = m;else{int p = j + 1;while (p <= m && a[i][p] > a[i][j])p = rgt[i][p] + 1;rgt[i][j] = p - 1;}if (i == n)bot[i][j] = n;else{int p = i + 1;while (p <= n && a[p][j] < a[i][j])p = bot[p][j] + 1;bot[i][j] = p - 1;}}}
}int main()
{//fre();for (int i = 1; i <= 2000; ++i)c[i] = c[i - 1] + i;for (int i = 1; i <= 1000; ++i){for (int j = 1; j <= 1000; ++j){d[i][j] = d[i][j - 1] + c[i + j - 1] - c[j - 1];}}scanf("%d", &casenum);for (casei = 1; casei <= casenum; ++casei){scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i){for (int j = 1; j <= m; ++j){scanf("%u", &a[i][j]);}}RMQinit();UI ans = 0;for (int i = 1; i <= n; ++i){for (int j = 1; j <= m; ++j){int topp = top[i][j];int bott = bot[i][j];int lftt = lft[i][j];int rgtt = rgt[i][j];ans += d[i - topp + 1][bott - i + 1] * d[j - lftt + 1][rgtt - j + 1] * a[i][j];}}printf("%u\n", ans);}return 0;
}
/*
【trick&&吐槽】
复杂度,复杂度,复杂度!
复杂度包括——
思维复杂度
代码复杂度
时空复杂度。这道题上来就想到求出每个点,作为鞍点,向左向右可以延展多少,向上向下可以延展多少。
不过是想用ST-RMQ实现。
而ST-RMQ的复杂度多了一个log(n)妈蛋,zimpha的数据竟然不水了,多个log的算法就tle了。
>.<虽然节省了思维复杂度,却浪费了代码复杂度与时空复杂度,最后没有AC。
还是酌情想出高效的算法比较好。【题意】
给你一个n*m的矩阵。
我们定义S(a,b)为所有大小为a*b的子矩阵的权值和。
一个子矩阵的权值和,定义为——
该矩阵中有所有鞍点的权值和。一个矩阵中的鞍点,定义为——
该点同行的所有点中,该点为唯一最小的点。
该点同列的所有点中,该点为唯一最大的点。让你输出∑(a=1~n)∑(b=1~m)a*b*S(a,b)【类型】
贪心思维 单调思维【分析】
这题用ST-RMQ的算法会导致tle
于是我们要尝试用O(nm)的算法AC这道题。
也就是说,用均摊O(1)的复杂度,求出——
1,每个点向左最多延伸多少个数,都比它大
2,每个点向右最多延伸多少个数,都比它大
3,每个点向上最多延伸多少个数,都比它小
4,每个点向下最多延伸多少个数,都比它小这个的处理方式,可以是本程序的暴力跳。
比如我们处理,"每个点向左最多延伸多少个数,都比它大"时,
int p = j - 1;
while (p >= 1 && a[i][p] > a[i][j])p = lft[i][p] - 1;
lft[i][j] = p + 1;我们对每个点,处理其向左可以延展的最大区间段。
如果发现再向左所延展的区间点,小于等于它,那延展失效。
否则便可以继续向左延展。
这个延展的过程,实际上也是区间合并。
显然我们一共只有n个区间,每次向左延展,也意味着多一次的区间合并。
而每个区间最多被合并一次,所以对于每行的m个数,处理lft[][]的总的复杂度为O(m)
即单点均摊复杂度为O(m),总复杂度为O(nm)当然,这个过程还可以通过单调栈实现。
以处理lft为例。
我们维护一个严格上升的单调栈。
对于每一个位点,
如果其之前栈顶元素大于它,那么该点的左延展区间范围,就可以覆盖到该栈顶元素,我们同时做出栈处理。
直到——使得其之前栈顶元素小于等于它,那么该点就无法向左延展了,其延展的极限也就知道了。因为每个点最多入栈一次,出栈一次。所以每点的均摊复杂度为O(1)=======================
当我们处理完每个点向左向右向上向下的最大延展范围后。
那这个点有贡献的矩阵,
向上的延展长度,可以是[1,top]
向下的延展长度,可以是[1,bot]
向左的延展长度,可以是[1,lft]
向右的延展长度,可以是[1,rgt]也就是说,包括该点的【时间复杂度&&优化】
O(nm)【数据】
100
5 5
3 0 7 9 10
4 9 9 9 7
7 1 7 1 10
3 1 8 6 1
2 2 6 7 10 */
【HDU5749 BestCoder Round 84C】【ST-RMQ?NO!单调栈法 + 贡献思维】Colmerauer 所有子矩阵size乘鞍点权值和
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1010, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
int a[N][N];
UI c[N + N];
UI d[N][N];
int lft[N][N];
int rgt[N][N];
int top[N][N];
int bot[N][N];int sta[N], tp;
void RMQinit()
{for (int i = 1; i <= n; ++i){sta[tp = 0] = 0; a[i][0] = -1e9;for (int j = 1; j <= m; ++j){while (a[i][sta[tp]] > a[i][j])--tp;lft[i][j] = sta[tp] + 1;//if (a[i][sta[tp]] == a[i][j])--tp;sta[++tp] = j;}}for (int i = 1; i <= n; ++i){sta[tp = 0] = m + 1; a[i][m + 1] = -1e9;for (int j = m; j >= 1; --j){while (a[i][sta[tp]] > a[i][j])--tp;rgt[i][j] = sta[tp] - 1;//if (a[i][sta[tp]] == a[i][j])--tp;sta[++tp] = j;}}for (int j = 1; j <= m; ++j){sta[tp = 0] = 0; a[0][j] = 1e9;for (int i = 1; i <= n; ++i){while (a[sta[tp]][j] < a[i][j])--tp;top[i][j] = sta[tp] + 1;//if (a[sta[tp]][j] == a[i][j])--tp;sta[++tp] = i;}}for (int j = 1; j <= m; ++j){sta[tp = 0] = n + 1; a[n + 1][j] = 1e9;for (int i = n; i >= 1; --i){while (a[sta[tp]][j] < a[i][j])--tp;bot[i][j] = sta[tp] - 1;//if (a[sta[tp]][j] == a[i][j])--tp;sta[++tp] = i;}}
}int main()
{for (int i = 1; i <= 2000; ++i)c[i] = c[i - 1] + i;for (int i = 1; i <= 1000; ++i){for (int j = 1; j <= 1000; ++j){//d[i][j] = d[i][j - 1] + c[i + j - 1] - c[j - 1]; //第一种计算方法d[i][j] = i*c[j] + j*c[i] - i*j; //第二种计算方法}}scanf("%d", &casenum);for (casei = 1; casei <= casenum; ++casei){scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i){for (int j = 1; j <= m; ++j){scanf("%d", &a[i][j]);}}RMQinit();UI ans = 0;for (int i = 1; i <= n; ++i){for (int j = 1; j <= m; ++j){int topp = top[i][j];int bott = bot[i][j];int lftt = lft[i][j];int rgtt = rgt[i][j];ans += d[i - topp + 1][bott - i + 1] * d[j - lftt + 1][rgtt - j + 1] * a[i][j];}}printf("%u\n", ans);}return 0;
}
/*
【trick&&吐槽】
复杂度,复杂度,复杂度!
复杂度包括——
思维复杂度
代码复杂度
时空复杂度。这道题上来就想到求出每个点,作为鞍点,向左向右可以延展多少,向上向下可以延展多少。
不过是想用ST-RMQ实现。
而ST-RMQ的复杂度多了一个log(n)妈蛋,zimpha的数据竟然不水了,多个log的算法就tle了。
>.<虽然节省了思维复杂度,却浪费了代码复杂度与时空复杂度,最后没有AC。
还是酌情想出高效的算法比较好。【题意】
给你一个n*m的矩阵。
我们定义S(a,b)为所有大小为a*b的子矩阵的权值和。
一个子矩阵的权值和,定义为——
该矩阵中有所有鞍点的权值和。一个矩阵中的鞍点,定义为——
该点同行的所有点中,该点为唯一最小的点。
该点同列的所有点中,该点为唯一最大的点。让你输出∑(a=1~n)∑(b=1~m)a*b*S(a,b)【类型】
贪心思维 单调思维【分析】
这题用ST-RMQ的算法会导致tle
于是我们要尝试用O(nm)的算法AC这道题。
也就是说,用均摊O(1)的复杂度,求出——
1,每个点向左最多延伸多少个数,都比它大
2,每个点向右最多延伸多少个数,都比它大
3,每个点向上最多延伸多少个数,都比它小
4,每个点向下最多延伸多少个数,都比它小这个的处理方式,可以是本程序的暴力跳。
比如我们处理,"每个点向左最多延伸多少个数,都比它大"时,
int p = j - 1;
while (p >= 1 && a[i][p] > a[i][j])p = lft[i][p] - 1;
lft[i][j] = p + 1;我们对每个点,处理其向左可以延展的最大区间段。
如果发现再向左所延展的区间点,小于等于它,那延展失效。
否则便可以继续向左延展。
这个延展的过程,实际上也是区间合并。
显然我们一共只有n个区间,每次向左延展,也意味着多一次的区间合并。
而每个区间最多被合并一次,所以对于每行的m个数,处理lft[][]的总的复杂度为O(m)
即单点均摊复杂度为O(m),总复杂度为O(nm)当然,这个过程还可以通过单调栈实现。
以处理lft为例。
我们维护一个严格上升的单调栈。
对于每一个位点,
如果其之前栈顶元素大于它,那么该点的左延展区间范围,就可以覆盖到该栈顶元素,我们同时做出栈处理。
直到——使得其之前栈顶元素小于等于它,那么该点就无法向左延展了,其延展的极限也就知道了。因为每个点最多入栈一次,出栈一次。所以每点的均摊复杂度为O(1)=======================
当我们处理完每个点向左向右向上向下的最大延展范围后,我们要开始算贡献了——对于每个点,这个点作为鞍点,那这个点所得到贡献的矩阵——
向上的延展长度,可以是[1,top]
向下的延展长度,可以是[1,bot]
向左的延展长度,可以是[1,lft]
向右的延展长度,可以是[1,rgt]
其贡献,也就是
int y=0, x=0;
for(int i=1;i<=top;++i)
{for(int j=1;j<=bot;++j){y+=(i+j-1);}
}
for(int i=1;i<=lft;++i)
{for(int j=1;j<=rgt;++j){x+=(i+j-1);}
}
ans+=y*x*a[i][j]我们的做法有以下几种——
1,预处理for(int i=1;i<=a;++i)for(int j=1;j<=b;++j)tmp+=i+j-1;
这个的预处理,d[i][j]是在d[i][j-1]的基础上,获得了c[i+j-1]-c[j-1]的增长
2,独立(i)与(j)算贡献,显然——for(int i=1;i<=a;++i)for(int j=1;j<=b;++j)tmp+=i+j-1;有b个(1~a),有a个(1~b),再减去a*b个1【时间复杂度&&优化】
O(nm)【数据】
100
5 5
3 0 7 9 10
4 9 9 9 7
7 1 7 1 10
3 1 8 6 1
2 2 6 7 10*/
这篇关于【HDU5749 BestCoder Round 84C】【ST-RMQ?NO!暴力跳法or单调栈法 + 贡献思维】Colmerauer 所有子矩阵size乘鞍点权值和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!