2.11 CW 模拟赛 T4. 刀压电线

news/2025/2/13 9:30:35/文章来源:https://www.cnblogs.com/YzaCsp/p/18712484

思路

题意

给定一个 n×mn \times m (1n,m2000)(1 \leq n, m \leq 2000) 的方格图, 每个方格可以是

  • 空地
  • 房屋
    一共有 p(1p2×105)p (1 \leq p \leq 2 \times 10^5)
  • 墙壁

定义两个房屋 u,vu, v 之间的距离为

uu 途径一些房屋 ((可以不经过)) 到达 vv
um1m2mkvu \to m_1 \to m_2 \to \cdots \to m_k \to v , 两个房屋之间经过的空地数量记为 xx
最终的距离即为 min{x1}\min \{x - 1\}

给定 q(1q2×105)q (1 \leq q \leq 2 \times 10^5) 次询问, 每次询问两个房屋之间的距离最小值

朴素想法是对于每两个房屋连边, 然后通过最小生成树解决
显然的, 连边的复杂度直接趋势, 但是我们不难发现, 每个点只需要连上可能在最小生成树上出现的边即可, 我们多源 \(\rm{bfs}\) , 每个点只连接与自己最近的边即可

所以我们连边 \(\mathcal{O} (nm)\)
建立最小生成树 \(\mathcal{O} (p^2)\) , 边的条数是 \(\mathcal{O} (nm)\) 的, 所以用优化的 \(\rm{prim}\) 做到 \(\mathcal{O} (nm \log p)\)
查询是 \(\rm{LCA}\) , 一共是 \(\mathcal{O} (q \log p)\)

实现

不好是码力题

框架

\(\rm{bfs}\)

每个点往外染色的同时记录距离, 碰上了就连边即可, 不管重边即可, 也管不了

最小生成树

新图上做 \(\rm{prim}\) 即可, 记录哪些边在 \(\rm{MST}\) 上, 最后新建一颗树

\(\rm{LCA}\)

倍增往上跳即可, 路上记录最小值, 在 \(\rm{LCA}\) 处合并
这个同样用倍增合并即可

代码

#include <bits/stdc++.h>
const int MAXN = 2005;
const int MAXM = 10000000;
const int MAXP = 2e5 + 20;int n, m, p, q;
int mp[MAXN][MAXN];
std::pair<int, int> pos[MAXP]; // i 座房屋的位置/*连边 + 建立最小生成树*/
class mstbuilder
{
private:/*图*/struct graph {struct node { int from, to, nxt, w; } edge[MAXM << 2]; int cnt = -1; int head[MAXP];void head_init() { memset(head, -1, sizeof head); }void addedge(int u, int v, int w) { edge[++cnt].from = u, edge[cnt].to = v, edge[cnt].w = w, edge[cnt].nxt = head[u], head[u] = cnt; }} gra; // 初始图std::pair<int, int> col[MAXN][MAXN];int dx[5] = {0, -1, 0, 1, 0}, dy[5] = {0, 0, -1, 0, 1};std::queue<std::pair<int, int>> Q;/*检查是否在地图*/ bool check(int x, int y) { return (x >= 1 && x <= n) && (y >= 1 && y <= m); }/*多源 bfs*/void bfs() {while (!Q.empty()) Q.pop();/*加入初始点*/ for (int i = 1; i <= p; i++) { Q.push(pos[i]); col[pos[i].first][pos[i].second] = {i, 0}; }while (!Q.empty()) {std::pair<int, int> tmp = Q.front(); Q.pop();/*拓展一层*/int x = tmp.first, y = tmp.second;for (int op = 1; op <= 4; op++) {int nx = x + dx[op], ny = y + dy[op];if (!check(nx, ny) || !(~mp[nx][ny])) continue;/*连边*/if (col[nx][ny].first) {if (col[nx][ny].first == col[x][y].first) continue;int w = col[nx][ny].second + col[x][y].second;gra.addedge(col[nx][ny].first, col[x][y].first, w), gra.addedge(col[x][y].first, col[nx][ny].first, w);} else col[nx][ny].first = col[x][y].first, col[nx][ny].second = col[x][y].second + 1, Q.push({nx, ny});}}}struct node {int id, dis, e; // 点的标号和边的长度, 编号node(int a, int b, int c) { id = a, dis = b, e = c; }friend bool operator < (const node &a, const node &b) { return a.dis > b.dis; }} ;std::priority_queue<node> PQ;std::vector<int> inMST;void prim(int st) {while (!PQ.empty()) PQ.pop(); PQ.push(node(st, 0, -1));int tmp = 0;while (!PQ.empty()) {node u = PQ.top(); PQ.pop();if (done[u.id]) continue; done[u.id] = cnt, tmp++;if (~u.e) inMST.push_back(u.e);for (int e = gra.head[u.id]; ~e; e = gra.edge[e].nxt) {node v (gra.edge[e].to, gra.edge[e].w, e);if (done[v.id]) continue;PQ.push(v);}}}public:/*建立初始图*/void buildgra() { gra.head_init(); bfs(); }graph mst; // 最小生成树int done[MAXP], cnt = 0; // 在哪颗 MST 中void buildmst() {memset(done, 0, sizeof done); mst.head_init();for (int i = 1; i <= p; i++)if (!done[i]) { inMST.clear(), ++cnt; prim(i); for (auto e : inMST) {int u = gra.edge[e].from, v = gra.edge[e].to, w = gra.edge[e].w;mst.addedge(u, v, w), mst.addedge(v, u, w);}}}
} bd;/*LCA 求答案*/
class solution
{
private:mstbuilder* bd_ptr;struct node {int fa[MAXP][20], lev[MAXP][20]; // 倍增数组int dep[MAXP];node() { memset(fa, 0, sizeof fa), memset(lev, 0, sizeof lev), memset(dep, 0, sizeof dep); }} mst;bool vis[MAXP];/*处理倍增数组 + 深度数组*/void dfs1(int u, int fat, int faw) {mst.dep[u] = mst.dep[fat] + 1;mst.fa[u][0] = fat; mst.lev[u][0] = faw;/*处理 fa*/ for (int i = 1; (1 << i) <= mst.dep[u]; i++) mst.fa[u][i] = mst.fa[mst.fa[u][i - 1]][i - 1];/*处理 lev*/ for (int i = 1; (1 << i) <= mst.dep[u]; i++) mst.lev[u][i] = std::max(mst.lev[u][i - 1], mst.lev[mst.fa[u][i - 1]][i - 1]);for (int e = bd_ptr->mst.head[u]; ~e; e = bd_ptr->mst.edge[e].nxt) {if (bd_ptr->mst.edge[e].to == fat) continue;dfs1(bd_ptr->mst.edge[e].to, u, bd_ptr->mst.edge[e].w);}}/*预处理需要的信息*/ void init() { for (int i = 1; i <= p; i++) if (!vis[bd_ptr->done[i]]) { vis[bd_ptr->done[i]] = true; mst.dep[0] = -1; dfs1(i, 0, 0);} }/*找到 LCA 并返回结果*/int LCA(int u, int v) {if (mst.dep[u] < mst.dep[v]) std::swap(u, v);int ans = 0;/*提到一起*/ for (int i = 19; i >= 0; i--) if (mst.dep[u] - (1 << i) >= mst.dep[v]) ans = std::max(ans, mst.lev[u][i]), u = mst.fa[u][i];/*同步上提*/ for (int i = 19; i >= 0; i--) if (mst.fa[u][i] != mst.fa[v][i]) ans = std::max(ans, mst.lev[u][i]), ans = std::max(ans, mst.lev[v][i]), u = mst.fa[u][i], v = mst.fa[v][i];return ans = ((u != v) ? std::max({ans, mst.lev[u][0], mst.lev[v][0]}) : ans);}public:solution() : bd_ptr(nullptr) {}solution(mstbuilder* bd) : bd_ptr(bd) {}/*处理问题*/void solve() {init();while (q--) {int u, v; scanf("%d %d", &u, &v);/*不连通*/ if (bd_ptr->done[u] != bd_ptr->done[v]) { printf("-1\n"); continue; }/*处理 LCA*/ printf("%d\n", LCA(u, v));}}} sol;int main()
{scanf("%d %d %d %d", &n, &m, &p, &q);char tmp; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { std::cin >> tmp; mp[i][j] = (tmp == '.' ? 0 : -1); }int tx, ty; for (int i = 1; i <= p; i++) { scanf("%d %d", &tx, &ty); pos[i] = {tx, ty}; mp[tx][ty] = i; }bd.buildgra();bd.buildmst();solution sol(&bd);sol.solve();return 0;
}

总结

两点间的最值型参数的最值化问题, 往往使用最值生成树
常用的简化最小生成树前的建图方式 : 多源 \(\rm{bfs}\)

非常好倍增, 爱来自瓷器

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

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

相关文章

7. Docker 容器数据卷的使用(超详细的讲解说明)

7. Docker 容器数据卷的使用(超详细的讲解说明) @目录7. Docker 容器数据卷的使用(超详细的讲解说明)1. Docker容器数据卷概述2. Docker 容器数据卷的使用演示:2.1 宿主 和 容器之间映射添加容器卷2.2 容器数据卷 读写规则映射添加说明2.3 容器数据卷的继承和共享3. 最后:坑:…

到底值不值得本地部署残血版DeepSeek?一文说清!教你如何白嫖满血版DeepSeek

一、介绍最近一段时间,DeepSeek 备受关注,夏天也向身边朋友推荐。但它常无法使用,原因是受到大规模恶意攻击,且 IP 地址在美国。 通过以下网站可以查看DeepSeek网站状态: status.deepseek.com/ 可以发现,最近标红的就是故障中​若遇服务器繁忙提示,大概率是被攻击了,并…

干货:DeepSeek+SpringAI实现流式对话!

前面一篇文章我们实现了《炸裂:SpringAI内置DeepSeek啦!》,但是大模型的响应速度通常是很慢的,为了避免用户用户能够耐心等待输出的结果,我们通常会使用流式输出一点点将结果输出给用户。 那么问题来了,想要实现流式结果输出,后端和前端要如何配合?后端要使用什么技术实…

Ftrans文件安全外发系统,为企业数据保驾护航!

随着企业的不断发展,集团分公司及各部门需向外部客户、合作伙伴及海外同事外发文件。过去,主要通过邮件、FTP方式将数据进行外发,主要存在以下问题和挑战: 1.进行文件外发时需通过OA的审批,由于OA审批与FTP传输两个环节割裂,公司无法有效限制数据外发范围和管控数据外发安…

本地部署 DeepSeek:小白也能轻松搞定!

大家好,我是晓凡。 写在前面 最近DeepSeek太火了,以至于每个小伙伴都想试试。DeepSeek 的到来可谓是开启了全民AI热潮。 本以为DeepSeek本地化部署有多难,实际上验证后很简单,操作起来就像给电脑装个新软件那么简单,大约十多分钟可完成本地部署。 今天咱们来聊聊如何在自己…

Git指南-从入门到精通

代码提交和同步命令 流程图如下:第零步: 工作区与仓库保持一致 第一步: 文件增删改,变为已修改状态 第二步: git add ,变为已暂存状态$ git status $ git add --all # 当前项目下的所有更改 $ git add . # 当前目录下的所有更改 $ git add xx/xx.py xx/xx2.py # 添加某几个…

一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Server 2019

一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Server 2019不熟悉整个流程的朋友可以先看之前的部署文章,手动部署一遍 一步步教你在CentOS 7.9上安装SQL Server 2019前言 这套Ansible脚本属于红帽官方出品,是一套mssql的自动化运维脚本,能够实现mssql的单实例部署和Alwa…

【Linux】Linux如何查看JDK的安装路径

如何在一台Linux服务器上查找JDK的安装路径呢?有那些方法可以查找定位JDK的安装路径?是否有一些局限性呢?下面总结了一下如何查找JDK安装路径的方法. 1、echo $JAVA_HOME 使用$JAVA_HOME的话能定位JDK的安装路径的前提是配置了环境变量$JAVA_HOME,否则如下所示,根本定位不…

[虚拟化/Docker] Docker Desktop 安装与使用

0 序:DeepSeek 等AI大模型在Windows的私有化部署DeepSeek 等AI大模型在Windows的私有化部署,最流行的开源AI终端应用————Dify,依赖于 Docker 环境。由此,必然离不开:Docker Desktop1 概述:Docker Desktopdocker desktop 是一款Docker容器运行管理工具,用于在本地机器…

【VUE框架】渗透测试的一些技巧(实现自动化测试)

以下文章来源于渗透测试之道 ,作者kk1230 一、什么是VUEVue.js 是一个渐进式 JavaScript 框架,用于构建用户界面。而 Webpack 是一个模块打包工具,用于将项目中的各种资源(如 JavaScript 模块、CSS 样式文件、图片等)打包成浏览器可以识别的文件。 Webpack概念机制Webpack…

案例 百万数据批量插入测试

1、jpa、mybatis-plus、jdbc、load data infile测试比较2、load data infile语法测试3、相关代码 测试package com.xm;import com.xm.entity.UserP; import com.xm.task.JdbcInsert2Task; import com.xm.task.JdbcInsertTask; import com.xm.task.MybatisPlusInsert2Task; impo…

案例 百万级数据批量插入测试

1、jpa、mybatis-plus、jdbc、load data infile测试比较2、load data infile语法测试注:测试有限,仅供参考。雨淋淋过的季节