Manhattan Triangle
纪念一下代码打得太慢了导致比赛结束3分钟才做出来的E题
我的做法:
考虑确定枚举三角形的一个点。最开始尝试枚举
形式化地,设三个点分别为
我们讨论一种情况:
可知这种情况又可分为两种情况,
画图:
我们发现,有长方形的周长为
所以我们发现,我们枚举了中间点之后,可以直接确定
不难有
一个比较自然的想法是我们用一个数据结构记录
于是这种情况就处理完了,其余情况类似处理
代码(代码能力比较弱,不会写函数封装,所以比较丑;实际上以上思路来自《算法竞赛进阶指南》CDQ分治的例题的代码,用的是函数封装):
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+10;
int n,d;
struct Point
{
int x,y,id;
}p[N];
map<pair<int,int>,int> mark;
map<int,queue<int> > vis;//注意这里要用队列存储所有满足条件的点
bool cmp(Point a,Point b)
{
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
bool cmp1(Point a,Point b)
{
if(a.x==b.x)return a.y>b.y;
return a.x<b.x;
}
bool cmp2(Point a,Point b)
{
if(a.x==b.x)return a.y<b.y;
return a.x>b.x;
}
bool cmp3(Point a,Point b)
{
if(a.x==b.x)return a.y>b.y;
return a.x>b.x;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
mark.clear();
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
p[i].id=i;
mark[make_pair(p[i].x,p[i].y)]=i;
}
bool flag=0;
vis.clear();
sort(p+1,p+n+1,cmp);
p[0].x=-1e6;
for(int i=1,last=1;i<=n;i++)
{
if(p[i].x!=p[i-1].x)
while(p[last].x<p[i].x-(d>>1))
{
vis[p[last].x+p[last].y].pop();
if(vis[p[last].x+p[last].y].empty())
{
map<int,queue<int> >::iterator it=vis.find(p[last].x+p[last].y);
vis.erase(it);
}
last++;
}
if(mark.find(make_pair(p[i].x+(d>>1),p[i].y-(d>>1)))!=mark.end()&&vis.find(p[i].x+p[i].y-d)!=vis.end())
{
flag=1;
printf("%d %d %d\n",p[i].id,mark[make_pair(p[i].x+(d>>1),p[i].y-(d>>1))],vis[p[i].x+p[i].y-d].front());
break;
}
vis[p[i].x+p[i].y].push(p[i].id);
}
if(flag) continue;
vis.clear();
sort(p+1,p+n+1,cmp1);
p[0].x=-1e6;
for(int i=1,last=1;i<=n;i++)
{
if(p[i].x!=p[i-1].x)
while(p[last].x<p[i].x-(d>>1))
{
vis[p[last].y-p[last].x].pop();
if(vis[p[last].y-p[last].x].empty())
{
map<int,queue<int> >::iterator it=vis.find(p[last].y-p[last].x);
vis.erase(it);
}
last++;
}
if(mark.find(make_pair(p[i].x+(d>>1),p[i].y+(d>>1)))!=mark.end()&&vis.find(-p[i].x+p[i].y+d)!=vis.end())
{
flag=1;
printf("%d %d %d\n",p[i].id,mark[make_pair(p[i].x+(d>>1),p[i].y+(d>>1))],vis[-p[i].x+p[i].y+d].front());
break;
}
vis[-p[i].x+p[i].y].push(p[i].id);
}
if(flag) continue;
vis.clear();
sort(p+1,p+n+1,cmp2);
p[0].x=1e6;
for(int i=1,last=1;i<=n;i++)
{
if(p[i].x!=p[i-1].x)
while(p[last].x>p[i].x+(d>>1))
{
vis[-p[last].y+p[last].x].pop();
if(vis[-p[last].y+p[last].x].empty())
{
map<int,queue<int> >::iterator it=vis.find(-p[last].y+p[last].x);
vis.erase(it);
}
last++;
}
if(mark.find(make_pair(p[i].x-(d>>1),p[i].y-(d>>1)))!=mark.end()&&vis.find(p[i].x-p[i].y+d)!=vis.end())
{
flag=1;
printf("%d %d %d\n",p[i].id,mark[make_pair(p[i].x-(d>>1),p[i].y-(d>>1))],vis[p[i].x-p[i].y+d].front());
break;
}
vis[p[i].x-p[i].y].push(p[i].id);
}
if(flag) continue;
vis.clear();
sort(p+1,p+n+1,cmp3);
p[0].x=1e6;
for(int i=1,last=1;i<=n;i++)
{
if(p[i].x!=p[i-1].x)
while(p[last].x>p[i].x+(d>>1))
{
vis[p[last].y+p[last].x].pop();
if(vis[p[last].y+p[last].x].empty())
{
map<int,queue<int> >::iterator it=vis.find(p[last].y+p[last].x);
vis.erase(it);
}
last++;
}
if(mark.find(make_pair(p[i].x-(d>>1),p[i].y+(d>>1)))!=mark.end()&&vis.find(p[i].x+p[i].y+d)!=vis.end())
{
flag=1;
printf("%d %d %d\n",p[i].id,mark[make_pair(p[i].x-(d>>1),p[i].y+(d>>1))],vis[p[i].x+p[i].y+d].front());
break;
}
vis[p[i].x+p[i].y].push(p[i].id);
}
if(!flag) puts("0 0 0");
}
return 0;
}
然后讲一下题解区说的曼哈顿距离转切比雪夫距离(蒟蒻以前没听过这个trick,学了一下,给没见过的同学看看)
将曼哈顿距离(Manhattan Distance)转化为切比雪夫距离(Chebyshev Distance)可以通过一种简单的坐标变换来实现。这种变换基于两者之间的数学关系,使得在变换后的坐标系中,曼哈顿距离等价于切比雪夫距离。
曼哈顿距离
曼哈顿距离定义为在标准的坐标系上,两个点在标准坐标系上的绝对轴距总和。对于二维空间中的两个点
切比雪夫距离
切比雪夫距离定义为两个点在向量空间中的各坐标数值差绝对值的最大值。对于二维空间中的两个点
坐标变换
为了将曼哈顿距离转化为切比雪夫距离,我们可以对原始坐标进行缩放和旋转(在二维情况下,实际上是通过缩放实现的,因为旋转不会改变曼哈顿距离和切比雪夫距离的等价性)。但是,更直接的方法是使用一种称为“等距嵌入”的技巧,即通过一个线性变换将原坐标系映射到一个新的坐标系中,使得在新的坐标系中曼哈顿距离等于切比雪夫距离。
具体来说,对于二维空间中的点,我们可以将每个点的坐标
证明
设
在新坐标系中,两点之间的切比雪夫距离为:
这可以简化为:
由于
但在这种情况下,等号成立,因为:考虑
以上内容来自文心一言,最后的证明稍作修改
于是进行坐标变换之后可以知道这道题目转化为:找出三个点
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!