看不懂大佬思路的看过来,本文将详细地带你讲解(图文结合)。本题解思路来自12345678hzx,为了方便理解,变量名大部分和 hzx 相同。
思路
前置知识:ST 表。运用 $n$ 维的 $minn$ 数组求最小值。 表示第
行第
个数及后面共
个数的最小值,这里不重点展开来讲。
部分代码
scanf("%lld",&n);
for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) scanf("%lld",&mmin[i][j][0]);
for(int k = 1;k <= n;k++) {
for(int j = 1;(1 << j) <= n;j++) {
for(int i = 1;i + (1 << (j - 1)) <= n;i++) {
mmin[k][i][j] = min(mmin[k][i][j - 1],mmin[k][i + (1 << (j - 1))][j - 1]);
}
}
}
接着,我们枚举长方形的左边,右边和底边,计算一共可以取几个。左边,右边我用 表示,底边用
表示。
分别表示上一个最小值小于
的行和上一个最小值等于
的行。下面的图片是初始情况。
如图所示,第一行 列到
列的最小值小于
,我们更新
。因为以
为底的不可能有,所以
不变。
如图所示,当 时最小值等于
。我们更新
。因为以
为底的共可以有
个,所以
。
如图所示,当 时最小值大于
。这时需要分类讨论:当
时
(与
时同理)。否则
不变(因为在取到最小值等于
的同时也会取到最小值小于
的)。
所以,经过每次操作后,我们只需要将 。
部分代码
for(int i = 1;i <= n;i++) llog[i] = log(i) / log(2);//表示 i 在二进制下的位数,需要预先储存避免重复运算。
for(int i = 1;i <= n;i++) {
for(int j = i;j <= n;j++) {
int x = 0,y = 0;
for(int k = 1;k <= n;k++) {
int v = llog[j - i + 1];
int s = min(mmin[k][i][v],mmin[k][j-(1<<v)+1][v]);//取出最小值
if(s < 100) y = k;
if(s == 100) x = k;
ans += max((long long)0,x - y);
}
}
}