洛谷P1191 矩形 题解

news/2025/2/11 22:22:55/文章来源:https://www.cnblogs.com/linjinkun/p/18710583

笛卡尔树的介绍

笛卡尔树,是一种二叉搜索树,它满足如下条件:

  • 每个节点的编号满足二叉搜索树的性质。
  • 每个节点的权值满足小根堆或大根堆的性质。

大概是这个样子:

笛卡尔树的建树

请看这里。

笛卡尔树的用途

它可以用来解决区间最值问题,它有一个重要性质:当这个笛卡尔树为小根堆时,\(\min_{i = l}^r a_i = a_{\operatorname{lca}(l,r)}\),当这个笛卡尔树为大根堆时,\(\max_{i = l}^r a_i = a_{\operatorname{lca}(l,r)}\)

所以,我们要求一个区间的最小值,只需将笛卡尔树建成小根堆的样式,按照性质求,如果要求一个区间的最大值,只需将笛卡尔树建成大根堆的样式,按照性质求。

当然,它还可以求有多少个区间的最小值或最大值为 \(a_i\),只需看有对少对区间的两端点的最近公共祖先是 \(i\) 即可,由于要使 \(\operatorname{lca}(l,r) = i\),那么 \(l\) 肯定在 \(i\) 的左子树或是 \(i\)\(r\) 肯定在 \(i\) 的右子树或是 \(i\),所以,设 \(s_i\) 表示 \(i\) 这个子树的大小,\(l_i\) 表示 \(i\) 的左儿子,\(r_i\) 表示 \(i\) 的右儿子,则就有 \((s_{l_i}+1) \times (s_{r_i}+1)\) 对区间的两端点的最近公共祖先为 \(i\)

此题做法

首先使用前缀和将每个位置往上有多少个连续的 H,设这个数组为 \(f\)
我们枚举每一行的每个位置。

然后放个假设图(其中一行):

图可能有点丑,请谅解(感谢)!!

假设这是第 \(K\) 行的 \(f\) 数组情况,其中第 \(i\) 个柱子的高度是 \(f_{K,i}\) 对于这第 \(K\) 行,如果要计算跟这个矩阵有关的矩阵有多少个,那么它必须得找高度比它高或相等的柱子拼起来才行,所以得找到最左边的一个柱子 \(j\),使得 \(j \le i\) 并且从 \(j\)\(i\) 这些柱子的高度都大于等于等于柱子 \(i\) 的高度,那么这个 \(j\) 就是高为第 \(i\) 个柱子的矩形的最小左端点,然后再找到 \(k\),使得 \(k<i\) 并且从 \(i\)\(k\) 这些柱子的高度都大于等于等于柱子 \(i\) 的高度,那么这个 \(k\) 就是高为第 \(i\) 个柱子的矩形的最大右端点,到这里大家都知道可以用单调栈做了吧,但是,我们是要用笛卡尔树的,所以还没完。实际上我们就是要找到区间长度最大的 \([j,k]\),使得 \(\min_{q = j}^k a_q = a_i\),那么就变成了上面说的笛卡尔树的用途的变形,由于我们要使 \([j,k]\) 的长度最大并且满足要求,那么 \(j\) 一定是 \(i\) 的左子树中编号最小的数(如果 \(i\) 没有左子树,那 \(j = i\)),\(k\) 一定是 \(i\) 的右子树中编号最大的数(如果 \(i\) 没有右子树,那 \(k = i\)),这样才能使 \([j,k]\) 长度最大且满足条件,知道了矩形最小左端点 \(j\) 和最大右端点 \(k\) 以及最大高度 \(f_{K,i}\),那 \((i-j+1) \times (k-i+1) \times f_{K,i}\) 就是包含第 \(i\) 个柱子的矩形个数,然后求一个子树中编号最小的数和编号最大的数只需找到笛卡尔树的根,然后搜索一下,递推即可。

时间复杂度可以说是题解区数一数二的了,\(O(n^2)\)

讲的这么详细,放个代码没问题吧:

#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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/882283.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

洛谷P2701 [USACO5.3] 巨大的牛棚Big Barn 题解

怎么题解全是清一色的 dp?可以用笛卡尔树啊(虽然麻烦了很多,但是我热爱)!笛卡尔树的介绍 笛卡尔树,是一种二叉搜索树,它满足如下条件:每个节点的编号满足二叉搜索树的性质。 每个节点的权值满足小根堆或大根堆的性质。大概是这个样子:笛卡尔树的建树 请看这里。 笛卡尔…

告别 DeepSeek 系统繁忙,七个 DeepSeek 曲线救国平替入口,官网崩溃也能用!

前言 DeepSeek作为一款备受瞩目的国产大模型,以其强大的功能和卓越的性能赢得了众多用户的青睐。然而,随着用户量的激增,DeepSeek官网近期频繁遭遇服务器繁忙甚至崩溃的问题,给广大用户带来了不小的困扰。面对这一现状,许多用户急于寻找能够替代或绕过官网限制的方法,以继…

第一章笔记

1.2CPU是寄存器的集合体 程序是把寄存器作为对象来描述的。 通常我们将汇编语言编写的程序转化成机器语言的过程称为汇编;反之,机器语言程序转化成汇编语言程序的过程则称为反汇编。 汇编语言采用助记符(memonic)来编写程序,每一个原本是电气信号的机器语言 指令都会有一个…

ARC191 192 题解

ARC191 题解 A - Replace Digits简要题意给定一个长为 \(n\) 的字符串 \(a\)。\(m\) 次操作,第 \(k\) 次给定一个字符 \(b_k\),你需要选择一个 \(i \in [1, n]\) 并将 \(a_i\) 替换成 \(b_k\)。 求最后能得到的所有字符串中,字典序最大的是什么。 \(1 \leq n, m \leq 10^6\)…

最近,测试的招聘市场已经疯掉了…

这是我入行测试的第1007天,门槛低,技术难度不高,食物链最底端。出现严重的bug,首先背锅的就是测试...📝 博主首页 : 「码上生花」 ,同名公众号 :「伤心的辣条」📝 面试求职: 「面试试题小程序」 ,内容涵盖 测试基础、Linux操作系统、MySQL数据库、Web功能测试、接口…

P1083 [NOIP 2012 提高组] 借教室(差分)

说实话竟然没想到还能这样差分,这道题我们需要二分查找m个订单,对于每次二分用一次差分,然后看如果只考虑1到mid个订单是否会出现教室不够用的情况,如果够用说明导致教室不够用的订单在后面,应该让begin=mid+1;反之让end-1;其实这道题就是让我们找第一个出现教室不够用的…

来吃糖

先放犯错后 be like:1 return // 倍增求 lca int bfs(int x) {queue<int> q;q.push(x);dep[x] = 1;while (q.size()) {x = q.front(), q.pop();for (auto it : G[x]) {int y = it.first, z = it.second;if (dep[y]) continue;dep[y] = dep[x] + 1;d[0][y] = z;f[0][y] =…

P9330 [JOISC 2023] JOI 国的节日 2 题解

Description 对于以下问题:给定长度为 \(n\) 的序列 \(a\)、\(b\),满足以下条件:在序列 \(a\) 与序列 \(b\) 中,\(1\) 到 \(2n\) 的整数各出现恰好一次; 对于 \(1\leq i\leq n\),\(a_i<b_i\); 对于 \(1\leq i<n\),\(a_i<a_{i+1}\)。求:最多能在 \([a_i,b_i]\…

一站式合同自动化:飞书审批与腾讯电子签的完美融合

Z国际教育中心专注于将全球顶尖教育资源引入中国,通过本地化整合与优化,将这些优质资源转化为中国青年触手可及的学习机会。我们的使命是帮助学生培养深厚的家国情怀与开阔的国际视野,助力他们成长为未来社会的栋梁之才。 遇到的问题 1. 业务种类多,合同审批繁琐 通过飞书平…

Linux下Docker及Nvidia Container ToolKit安装教程

作者:SkyXZ CSDN:SkyXZ~-CSDN博客 博客园:SkyXZ - 博客园 我们接下来在Ubuntu中安装Docker(安装详见:Get Docker | Docker Docs)及NVIDIA Container Toolkit(安装详见:Installing the NVIDIA Container Toolkit — NVIDIA Container Toolkit 1.17.3 documentat…

在用 uni-app 开发钉钉小程序的时候遇到一个奇怪的问题,发送请求拿不到返回的数据

今天我一位同事说用 uni-app 新开发的钉钉小程序里发送请求拿不到返回的数据,看了下发现调试工具的“Network”栏里显示请求是发送成功的,也有返回数据,但是没触发请求的回调函数。 原本用的是 luch-request 这个库发送的请求,后来试了下 uni-app 内置的 uni.request 以及钉…

15. 进程处理

一、什么是进程进程(Process)是正在运行的程序,是操作系统进行资源分配的基本单位。程序是存储在硬盘或内存的一段二进制序列,是静态的,而进程是动态的。每个进程都由自己的地址空间、代码段、数据段以及分配给它的其它系统资源(如文件描述符、网络连接等)。 二、创建子…