当前位置: 首页 > news >正文

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

老逗找基友 (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.hskmm.com/?act=detail&tid=9849

相关文章:

  • Caused by: java.lang.ClassNotFoundException: org.apache.rocketmq.remoting.common.RemotingUtil
  • VAE In JAX【个人记录向】
  • BLE蓝牙配网双模式实操:STA+SoftAP技术原理与避坑指南
  • 第58天:RCE代码amp;命令执行amp;过滤绕过amp;异或无字符amp;无回显方案amp;黑白盒挖掘
  • 057-Web攻防-SSRFDemo源码Gopher项目等
  • 060-WEB攻防-PHP反序列化POP链构造魔术方法流程漏洞触发条件属性修改
  • 059-Web攻防-XXE安全DTD实体复现源码等
  • 061-WEB攻防-PHP反序列化原生类TIPSCVE绕过漏洞属性类型特征
  • 051-Web攻防-文件安全目录安全测试源码等
  • Dilworth定理及其在算法题中的应用
  • 050-WEB攻防-PHP应用文件包含LFIRFI伪协议编码算法无文件利用黑白盒
  • error: xxxxx does not have a commit checked out
  • 049-WEB攻防-文件上传存储安全OSS对象分站解析安全解码还原目录执行
  • 云原生周刊:MetalBear 融资、Chaos Mesh 漏洞、Dapr 1.16 与 AI 平台新趋势
  • AI一周资讯 250913-250919
  • 045-WEB攻防-PHP应用SQL二次注入堆叠执行DNS带外功能点黑白盒条件-cnblog
  • linux 命令语句
  • 用 Kotlin 实现英文数字验证码识别
  • 达芬奇(DaVinci Reslove)字体文件 bugb标签
  • 语音芯片怎样挑选?语音芯片关键选型要点?
  • KingbaseES Schema权限及空间限额
  • HTTP库开发实战:核心库与httpplus扩展库示例解析
  • QMT交易系统向服务器同步订单丢失问题排查
  • 笔记1
  • 用 Python 和 Tesseract 实现英文数字验证码识别
  • 实用指南:OSPF特殊区域、路由汇总及其他特性
  • 禅道以及bug
  • 中电金信 :MCP在智能体应用中的挑战与对策
  • 第一次参与开源的时序数据库 IoTDB Committer:这份成就感是无可替代的
  • ECT-OS-JiuHuaShan 框架元推理的意义、价值、作用、应用场景和哲学理念的充分阐述:AGI奇点