解题报告-老逗找基友 (friends)

news/2025/9/19 18:41:03/文章来源:https://www.cnblogs.com/Starpop/p/19101491

老逗找基友 (friends)

题目背景

吴老逗有 \(n\) 个基友,位于平面直角坐标系的整点上。每个基友已与其最近的基友(如有多个则取编号最小)建立了双向心灵感应。但这样形成的网络可能不连通,因此吴老逗可以使用爱之魔法(连接自己与某个基友)来使网络连通。目标是使用最少的魔法次数使网络连通,并最小化连通网络中任意两个基友之间的最大距离(这里距离指感应次数,即边数)。

题目描述

给定 \(n\) 个点 \((x_i, y_i)\),点 \(i\) 与点 \(j\) 的距离定义为曼哈顿距离 \(|x_i - x_j| + |y_i - y_j|\)。初始时,每个点都连接其最近点(多个最近点时取编号最小)。这样得到一个图(可能不连通)。现在可以添加若干条边(每条边连接吴老逗(编号0)和某个基友),要求添加最少的边使图连通,并求连通后图的直径(即任意两点间最短路径的最大值)的最小可能值。

输入输出格式

输入格式(从 friends.in 读取):

  • 第一行:正整数 \(n\)
  • 接下来 \(n\) 行:每行两个整数 \(x, y\)

输出格式(写入 friends.out):

  • 一行:一个正整数,表示两个基友相隔感应次数的最小值的最大值(即最小可能直径)。

输入样例

5
1 1
2 2
4 4
4 5
2 3

输出样例

4

解题报告

阴间的码农题和缝合怪。

题目分为两部分:找曼哈顿距离下的最近点对、计算树的直径。

先处理曼哈顿距离下的最近点对,这也是一个经典的问题了。

老样子,遇到绝对值,直接分讨打开。

假设查询的点为 \((x_0,y_0)\),目前存在的点为 \((x_i,y_i)\),分讨打开绝对值,只有以下情况:

  1. \(x_0 \geq x_i\)\(y_0 \geq y_i\),\(|x_0-x_i|+|y_0-y_i|=(x_0+y_0)-(x_i+y_i)\),只需使符合条件的点(左下区域)的 \(x+y\) 最大;
  2. \(x_0 \geq x_i\)\(y_0 \leq y_i\),\(|x_0-x_i|+|y_0-y_i|=(x_0-y_0)-(x_i-y_i)\),只需使符合条件的点(左上区域)的 \(x-y\) 最大;
  3. \(x_0 \leq x_i\)\(y_0 \geq y_i\),\(|x_0-x_i|+|y_0-y_i|=(y_0-x_0)-(y_i-x_i)\),只需使符合条件的点(右下区域)的 \(y-x\) 最大;
  4. \(x_0 \leq x_i\)\(y_0 \leq y_i\),\(|x_0-x_i|+|y_0-y_i|=(x_i+y_i)-(x_0+y_0)\),只需使符合条件的点(右上区域)的 \(x+y\) 最小;

不过,我们不需要专门对每种情况都处理,实际上,通过将坐标系翻转,我们可以把其他的情况统一到第一种情况。所以,接下来只对第一种情况(左下区域)进行分析。

显然,如果一个点 \(i\) 被选为最近点,它必然在查询之前出现,满足:\(x_i\leq x_0,y_i\leq y_0\),并且有 \(|x_0-x_i|+|y_0-y_i|=(x_0+y_0)-(x_i+y_i)\),其中 \((x_0+y_0)\) 为定值,那么就只需要 \(x_i+y_i\) 最大就可以了。

注意为了满足偏序条件,我们需要把每个点按 \(x\) 坐标升序,同时用树状数组维护 \(y\) 坐标,坐标变换后计算四次。

要注意:

  1. \(x\) 坐标升序排序时,也要将 \(y\) 坐标作为第二关键字排序。
  2. 需要对变换前和后的每一个坐标都离散化。

显然,建出图后一定是一个森林

然后因为要求使用最少的魔法次数使网络连通且连通后图的直径最小,显然直接将每棵树的直径的中心和节点 \(0\) 直接相连是最优的。

接下来就是两次广搜找出每一棵树 的直径,答案就是 $ \max( \dfrac { \text{最长直径+次长直径} }{2}+2,\text{最长直径} ) $。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=101100;#define ckmax(x,y) ( x=max(x,y) )
#define ckmin(x,y) ( x=min(x,y) )inline int read()
{int f=1,x=0; char ch=getchar();while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }while(isdigit(ch))  { x=x*10+ch-'0';    ch=getchar(); }return f*x;
}
// ---------基本数据--------------------------------------------------
struct point
{int pos;        // 编号int x,y,X,Y;    // 坐标int px,py;      // 坐标排名
}p[N];
int n,ans;
int NUM[N<<2],top;  // 离散化数组
int Mx,My;// ---------向量表示边-----------------------------------------------------------
vector<int> e[N];inline void add_edge(int u,int v)
{e[u].push_back(v);e[v].push_back(u);
}// ---------并查集---------------------------------------------------------------int dad[N];int GetSet(int x)
{if(dad[x]==x) return x;return dad[x]=GetSet(dad[x]);
}inline void Merge(int u,int v)
{u=GetSet(u),v=GetSet(v);dad[v]=u;
}// ---------建图------------------------------------------------------------------#define lowbit(x) ( x&(-x) )
struct node
{int p,x;
}T[N<<2]; // 树状数组维护 x+y 的最大值。
node tp[N<<2];  // 每个点的最近点。// 结构体 node 的"max"
inline void ckmx(node &u,node v)
{if(u.x<v.x)return (void)( u=v );if(u.x==v.x && v.p<u.p)return (void)( u=v );
}// 结构体 node 的"min"
inline void ckmn(node &u,node v)
{if(u.x>v.x)return (void)( u=v );if(u.x==v.x && v.p<u.p)return (void)( u=v );
}// 树状数组维护最大值
inline void update(int x,int pos,int val)
{node tmp=(node){ pos,val };for(;x<=top;x+=lowbit(x))ckmx(T[x],tmp);
}// 查询最大值及其位置
inline node query(int x)
{node ans=(node){ 0,0 };for(;x>0;x-=lowbit(x))ckmx(ans,T[x]);return ans;
}// 维护横坐标的偏序
bool rule(point u,point v)
{if(u.px==v.px) return u.py<v.py;return u.px<v.px;
}// 用一次排序维护横坐标的偏序,用树状数组维护纵坐标的偏序
// 当 x<x0,y<y0 时,|x0-x|+|y0-y|=(x0+y0)-(x+y)
// 所以左下方的最近点的 x+y 一定最大
inline void solve()
{sort(p+1,p+n+1,rule);for(int i=1;i<=n;i++){node pos=query(p[i].py);if(pos.p){pos.x=abs(p[i].x+p[i].y-pos.x);node tmp=(node){ p[i].pos,pos.x };ckmn(tp[p[i].pos],pos);}update(p[i].py,p[i].pos,p[i].x+p[i].y);}for(int i=1;i<=top;i++) T[i]=(node){ 0,0 };
}inline void debug()
{for(int i=1;i<=n;i++)printf("%d %d\n",i,tp[i].p);puts("");
}// 建图的主函数,分讨打开绝对值,用坐标翻转统一处理
inline void BuildGraph()
{for(int i=1;i<=n;i++){NUM[++top]=p[i].x;  NUM[++top]=Mx-p[i].x;NUM[++top]=p[i].y;  NUM[++top]=My-p[i].y;tp[i].x=INF;dad[i]=i;}sort(NUM+1,NUM+top+1);top=unique(NUM+1,NUM+top+1)-NUM-1;for(int i=1;i<=n;i++){p[i].x=p[i].X,p[i].y=p[i].Y;p[i].px=lower_bound(NUM+1,NUM+top+1,p[i].x)-NUM;p[i].py=lower_bound(NUM+1,NUM+top+1,p[i].y)-NUM;}solve(); //debug();for(int i=1;i<=n;i++){p[i].x=Mx-p[i].X+1,p[i].y=p[i].Y;p[i].px=lower_bound(NUM+1,NUM+top+1,p[i].x)-NUM;p[i].py=lower_bound(NUM+1,NUM+top+1,p[i].y)-NUM;}solve(); //debug();for(int i=1;i<=n;i++){p[i].x=p[i].X,p[i].y=My-p[i].Y+1;p[i].px=lower_bound(NUM+1,NUM+top+1,p[i].x)-NUM;p[i].py=lower_bound(NUM+1,NUM+top+1,p[i].y)-NUM;}solve(); //debug();for(int i=1;i<=n;i++){p[i].x=Mx-p[i].X+1,p[i].y=My-p[i].Y+1;p[i].px=lower_bound(NUM+1,NUM+top+1,p[i].x)-NUM;p[i].py=lower_bound(NUM+1,NUM+top+1,p[i].y)-NUM;}solve(); //debug();for(int i=1;i<=n;i++){add_edge(i,tp[i].p);Merge(i,tp[i].p);}
}//---------森林中每颗树的直径----------------------------------------------------int dia[N],dis[N],ngr;
bool vis[N];// 广搜找最远点
inline int bfs(int s)
{memset(dis,-1,sizeof(dis));queue<int> q; q.push(s);int pos=s,mdis=dis[s]=0;while(!q.empty()){int u=q.front(); q.pop();if(dis[u]>mdis) mdis=dis[u],pos=u;for(int i=0;i<e[u].size();i++){int v=e[u][i];if(dis[v]==-1){dis[v]=dis[u]+1;q.push(v);}}}return pos;
}// 两遍广搜找到直径
inline void Getdia(int pos)
{int s=bfs(pos);int t=bfs(s);dia[++ngr]=dis[t];vis[GetSet(pos)]=true;
}// 得到所有直径
inline void GetED()
{for(int i=1;i<=n;i++)if(!vis[GetSet(i)])Getdia(i);
}signed main()
{freopen("friends.in","r",stdin);freopen("friends.out","w",stdout);n=read();for(int i=1;i<=n;i++){int x=read()+1,y=read()+1;p[i]=(point){ i,x,y,x,y,0,0 };ckmax(Mx,x),ckmax(My,y);}BuildGraph();GetED();sort(dia,dia+ngr+1);ans=dia[ngr];if(ngr>1) ckmax(ans,(dia[ngr]+1)/2+(dia[ngr-1]+1)/2+2);printf("%d",ans);return 0;
}

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

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

相关文章

Python_occ 学习记录 | 细观建模(1) - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

深入解析:uv:用 Rust 重写的极速 Python 包管理器

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Caused by: java.lang.ClassNotFoundException: org.apache.rocketmq.remoting.common.RemotingUtil

前言 启动报错,打包可以,启动报错; 针对这个问题 可以看下.ClassNotFoundException和NoClassDefFoundError:有啥区别先 org.springframework.beans.factory.BeanCreationException: Error creating bean with name …

VAE In JAX【个人记录向】

和上一篇 SAC In JAX 一样,我们用 JAX 实现 VAE,配置一样,只需要安装符合版本的 torchvision 即可,实现中提供了 tensorboard 记录以及最后的可视化展示,测试集即为最经典的 MNIST,代码如下: import jax import…

BLE蓝牙配网双模式实操:STA+SoftAP技术原理与避坑指南

想让设备同时支持蓝牙快速配网与AP热点备份?STA+SoftAP双模式是关键!本文深度解析技术原理,结合真实项目案例总结实操避坑点,助你一文搞懂双模式配网逻辑,开发少走90%弯路。 本文特别分享蓝牙配网方案: 以Air800…

【小白也能懂】PyTorch 里的 0.5 到底是干啥的?——一次把 Normalize 讲透! - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

第58天:RCE代码amp;命令执行amp;过滤绕过amp;异或无字符amp;无回显方案amp;黑白盒挖掘

第58天:RCE代码&命令执行&过滤绕过&异或无字符&无回显方案&黑白盒挖掘 案例演示RCE & 代码执行 & 命令执行 RCE-利用&绕过&异或&回显 白盒-CTF-RCE代码命令执行 黑盒-运行-R…

057-Web攻防-SSRFDemo源码Gopher项目等

057-Web攻防-SSRF&Demo源码&Gopher项目等 知识点知识点: 1、SSRF-原理-外部资源加载 2、SSRF-利用-伪协议&无回显 3、SSRF-挖掘-业务功能&URL参数案例演示1、SSRF-原理&挖掘&利用&修复 2…

060-WEB攻防-PHP反序列化POP链构造魔术方法流程漏洞触发条件属性修改

060-WEB攻防-PHP反序列化&POP链构造&魔术方法流程&漏洞触发条件&属性修改 知识点: 1、PHP-反序列化-应用&识别&函数 2、PHP-反序列化-魔术方法&触发规则 3、PHP-反序列化-联合漏洞&P…

059-Web攻防-XXE安全DTD实体复现源码等

059-Web攻防-XXE安全&DTD实体&复现源码等 知识点 XML&XXE-传输-原理&探针&利用&玩法 XML&XXE-黑盒-JS&黑盒测试&类型修改 XML&XXE-白盒-CMS&PHPSHE&无回显什么是XML?…

061-WEB攻防-PHP反序列化原生类TIPSCVE绕过漏洞属性类型特征

061-WEB攻防-PHP反序列化&原生类TIPS&CVE绕过漏洞&属性类型特征知识点 1、PHP-反序列化-属性类型&显示特征 2、PHP-反序列化-CVE绕过&字符串逃逸 3、PHP-反序列化-原生类生成&利用&配合1、…

051-Web攻防-文件安全目录安全测试源码等

051-Web攻防-文件安全&目录安全&测试源码等 知识点1、文件安全-前后台功能点-下载&读取&删除 2、目录安全-前后台功能点-目录遍历&目录穿越演示案例:➢文件安全-下载&删除-案例黑白盒 ➢目录…

Dilworth定理及其在算法题中的应用

1. Dilworth定理 Dilworth定理由数学家Robert P. Dilworth于1950年提出,它描述了偏序集中链和反链之间的关系。偏序集:一个集合 equipped with a partial order(即一个自反、反对称、传递的关系)。 链:偏序集的一…

error: xxxxx does not have a commit checked out

$ git commit -m "test" *error: AW30N does not have a commit checked outfatal: updating files failed解决方法: 删除多余的文件夹AW30N

049-WEB攻防-文件上传存储安全OSS对象分站解析安全解码还原目录执行

049-WEB攻防-文件上传&存储安全&OSS对象&分站&解析安全&解码还原&目录执行-cnblog#文件-解析方案-执行权限&解码还原 1、执行权限文件上传后存储目录不给执行权限 原理:开启禁止目录执行…

云原生周刊:MetalBear 融资、Chaos Mesh 漏洞、Dapr 1.16 与 AI 平台新趋势

云原生热点 MetalBear 获得 1250 万美元种子轮融资,推动 Kubernetes 开发解决方案 以色列初创公司 MetalBear 宣布完成 1250 万美元种子轮融资,由 TLV Partners 领投,TQ Ventures、MTF、Netz Capital 及多位知名天使…

AI一周资讯 250913-250919

原文: https://mp.weixin.qq.com/s/bnJ-kyOojPi6rqgx0NOXxg 阿里版Cursor正式收费!Qoder全球推出付费订阅,小白用了都说“最懂我” 2025年9月15日,阿里AI编程平台Qoder(被称为“阿里版Cursor”)面向全球用户正式推…

045-WEB攻防-PHP应用SQL二次注入堆叠执行DNS带外功能点黑白盒条件-cnblog

045-WEB攻防-PHP应用&SQL二次注入&堆叠执行&DNS带外&功能点&黑白盒条件-cnblog PHP-MYSQL-二次注入-DEMO&74CMS1、DEMO-用户注册登录修改密码1.注册新用户时,将注入的内容包含在注册的用户名…

linux 命令语句

rt 快csp初赛了,发现s组第一题一般是linux 命令语句,碎屑。 c cat:连接和显示文件内容 cd:切换工作目录 chmod:修改文件或目录的权限 chown:修改文件或目录的所有者 cp:复制文件或目录 d df/du:显示磁盘使用情…

用 Kotlin 实现英文数字验证码识别

在本教程中,我们将使用 Kotlin 和 Tesseract OCR 库实现对英文数字验证码的识别。Tesseract 是一个开源的 OCR 引擎,能够从图像中提取文本内容。结合 Kotlin 的简洁语法,我们可以高效地完成这个任务。环境准备 (1)…