题解:CF1979E Manhattan Triangle

· · 题解

我的做法:

考虑确定枚举三角形的一个点。最开始尝试枚举x最大的点,但是后面发现不太好讨论,于是尝试枚举x在中间的点,此时发现由于曼哈顿是三角形不可能是钝角三角形,剩下两个点要么同时在中间点的上方,要么同时在中间点的下方

形式化地,设三个点分别为 (x_1,y_1),(x_2,y_2),(x_3,y_3),其中 x_1≤x_2≤x_3,那么有 y_1≤y_2y_3≤y_2 或者 y_1≥y_2y_3≥y_2

我们讨论一种情况:y_1≤y_2y_3≤y_2,另一种情况同理

可知这种情况又可分为两种情况,y_2≤y_3 或者 y_3≤y_2,我们仍然讨论第一种情况另一种情况同理

画图:

我们发现,有长方形的周长为 3d,于是长加宽为 \frac{3d}{2},也就是 AF+AC=\frac{3d}{2},而我们有 AF+AB=d,于是有 BC=\frac{d}{2},又因为 BC+CD=d,于是 DC=\frac{d}{2}

所以我们发现,我们枚举了中间点之后,可以直接确定 x 最大的点,现在只用确定最后一个点就好了

不难有 x_2-x_1+y_2-y_1=d,即 x_2+y_2-d=x_1+y_1,或者 x_3+y_3-d=x_1+y_1

一个比较自然的想法是我们用一个数据结构记录 x<x_2 的所有点,然后直接查找就好了。但是这样会出现一个问题,就是如果我们查找出来的点的纵坐标大于y_3怎么办?实际上如果采用一些小技巧,就不会出现这种情况。我们发现 AC≤d,所以 AB≤\frac{d}{2},如果我们只保存横坐标在 AB 范围内的点,那么此时对数据结构中任意一个点 (x,y),有 x_3-x≤d,如果y>y_3那么 y_3+x_3-x<d+y,也就是 y_3+x_3-d<x+y不可能满足上面的等式,所以不用担心

于是这种情况就处理完了,其余情况类似处理

代码(代码能力比较弱,不会写函数封装,所以比较丑;实际上以上思路来自《算法竞赛进阶指南》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)可以通过一种简单的坐标变换来实现。这种变换基于两者之间的数学关系,使得在变换后的坐标系中,曼哈顿距离等价于切比雪夫距离。

曼哈顿距离

曼哈顿距离定义为在标准的坐标系上,两个点在标准坐标系上的绝对轴距总和。对于二维空间中的两个点 P_1(x_1, y_1) P_2(x_2, y_2) ,曼哈顿距离 D_M 定义为:

D_M(P_1, P_2) = |x_1 - x_2| + |y_1 - y_2|

切比雪夫距离

切比雪夫距离定义为两个点在向量空间中的各坐标数值差绝对值的最大值。对于二维空间中的两个点 P_1(x_1, y_1) P_2(x_2, y_2) ,切比雪夫距离 D_C 定义为:

D_C(P_1, P_2) = \max(|x_1 - x_2|, |y_1 - y_2|)

坐标变换

为了将曼哈顿距离转化为切比雪夫距离,我们可以对原始坐标进行缩放和旋转(在二维情况下,实际上是通过缩放实现的,因为旋转不会改变曼哈顿距离和切比雪夫距离的等价性)。但是,更直接的方法是使用一种称为“等距嵌入”的技巧,即通过一个线性变换将原坐标系映射到一个新的坐标系中,使得在新的坐标系中曼哈顿距离等于切比雪夫距离。

具体来说,对于二维空间中的点,我们可以将每个点的坐标 (x, y) 映射为 (x+y, x-y)(注意,这不是唯一的映射方式,但它是有效的一种)。在新的坐标系中,两点之间的曼哈顿距离将等于它们之间的切比雪夫距离。

证明

P_1(x_1, y_1) P_2(x_2, y_2) 在新坐标系中对应的点为 P_1'(x_1+y_1, x_1-y_1) P_2'(x_2+y_2, x_2-y_2)

在新坐标系中,两点之间的切比雪夫距离为:

D_C'(P_1', P_2') = \max(|(x_1+y_1) - (x_2+y_2)|, |(x_1-y_1) - (x_2-y_2)|)

这可以简化为:

D_C'(P_1', P_2') = \max(|x_1 - x_2 + y_1 - y_2|, |x_1 - x_2 - y_1 + y_2|)

由于 |a+b| \leq |a| + |b| |a-b| \leq |a| + |b| ,我们可以得出:

\max(|x_1 - x_2 + y_1 - y_2|, |x_1 - x_2 - y_1 + y_2|) \leq |x_1 - x_2| + |y_1 - y_2|

但在这种情况下,等号成立,因为:考虑 |a+b| \leq |a| + |b| |a-b| \leq |a| + |b| 的取等条件,若 a,b符号相同则前者取等,否则后者取等,于是讨论 |x_1 - x_2||y_1 - y_2|的符号就可以知道等号成立

以上内容来自文心一言,最后的证明稍作修改

于是进行坐标变换之后可以知道这道题目转化为:找出三个点 (x_1,y_1),(x_2,y_2),(x_3,y_3),使得 \max(|x_1-x_2|,|y_1-y_2|)=\max(|x_1-x_3|,|y_1-y_3|)=\max(|x_2-x_3|,|y_2-y_3|)=d,分类讨论,不妨设 x_1≤x_2≤x_3,如果说 |x_1-x_3|<d,那么就有三个点的y两两距离都为 d,这显然不可能,所以有 x_3-x_1=d,同理讨论可以知道 y_1=y_3,于是随便用数据结构维护一下就好了