P1191 矩形 题解
笛卡尔树的介绍
笛卡尔树,是一种二叉搜索树,它满足如下条件:
- 每个节点的编号满足二叉搜索树的性质。
- 每个节点的权值满足小根堆或大根堆的性质。
大概是这个样子:
笛卡尔树的建树
请看这里。
笛卡尔树的用途
它可以用来解决区间最值问题,它有一个重要性质:当这个笛卡尔树为小根堆时,
所以,我们要求一个区间的最小值,只需将笛卡尔树建成小根堆的样式,按照性质求,如果要求一个区间的最大值,只需将笛卡尔树建成大根堆的样式,按照性质求。
当然,它还可以求有多少个区间的最小值或最大值为
此题做法
首先使用前缀和将每个位置往上有多少个连续的 H,设这个数组为
然后放个假设图(其中一行):
图可能有点丑,请谅解(感谢)!!
假设这是第
时间复杂度可以说是题解区数一数二的了,
讲的这么详细,放个代码没问题吧:
#include<bits/stdc++.h>
using namespace std;
const int N = 155;
char a[N][N];
int q[N];
int f[N][N];
int l[N];
int r[N];
int depmax[N];
int depmin[N];
int num[N];
void dfs(int x)
{
depmax[x] = x;
depmin[x] = x;
if(l[x])
{
dfs(l[x]);
depmin[x] = min(depmin[x],depmin[l[x]]);
depmax[x] = max(depmax[x],depmax[l[x]]);
}
if(r[x])
{
dfs(r[x]);
depmin[x] = min(depmin[x],depmin[r[x]]);
depmax[x] = max(depmax[x],depmax[r[x]]);
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i<=n;i++)
{
scanf("%s",a[i]+1);
}
for(int i = 1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
{
if(a[i-1][j] == 'W'&&a[i][j] == 'W')
{
f[i][j] = f[i-1][j]+(a[i][j] == 'W');
}
else if(a[i][j] == 'W')
{
f[i][j] = 1;
}
}
}
int maxx = 0;
for(int i = 1;i<=n;i++)
{
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
int t = 0;
for(int j = 1;j<=n;j++)
{
while(t&&f[i][q[t]]>f[i][j])
{
l[j] = q[t];
t--;
}
if(t)
{
r[q[t]] = j;
}
q[++t] = j;
}
for(int j = 1;j<=n;j++)
{
num[l[j]] = i;
num[r[j]] = i;
}
for(int j = 1;j<=n;j++)
{
if(num[j]!=i)
{
dfs(j);
break;
}
}
for(int j = 1;j<=n;j++)
{
int ll,rr;
if(l[j])
{
ll = min(depmin[l[j]],j);
}
else
{
ll = j;
}
if(r[j])
{
rr = max(depmax[r[j]],j);
}
else
{
rr = j;
}
maxx+=(j-ll+1)*(rr-j+1)*f[i][j];
}
}
printf("%d",maxx);
return 0;
}