题目链接:点击查看
题目大意:给出一个大小为 r * c 的二维平面,再给出 n 个点,接下来有 m 次询问,没出询问会给出一个点 ( x , y ) ,需要计算出 ans[ i ] 为距离点 ( x , y ) 最远的那个点的距离,现在要求求出最小的那个 ans[ i ] 的值,并且输出其下标
题目分析:读完题想用 KDtree,去网上找了个模板套上发现会 T,然后就只能中规中矩去推公式了
假设以某个询问点 ( x , y ) 为原点来说,对于任意一点 ( a , b ) 与其距离分为四种情况讨论:
- 位于第一象限:a - x + b - y
- 位于第二象限:x - a + b - y
- 位于第三象限:x - a + y - b
- 位于第四象限:a - x + y - b
所以对于某个询问 ( x , y ) 来说,我们只需要将其分为四个象限,然后分别统计答案维护最大值即可
但是乍一看并没有在时间复杂度上进行优化,因为整体算法还是 O( n * n ) 的,而题目中只需要找到 “平面最远点对” 即可,所以将上面四种情况移一下项:
- 位于第一象限:(a + b)+(-x - y)
- 位于第二象限:(-a + b)+(x - y)
- 位于第三象限:(-a - b)+(x + y)
- 位于第四象限:(a - b)+(-x + y)
换句话说,对于某次询问的 ( x , y ) 来说,因为将所有的距离拆分成两个式子的加和,显然最远距离肯定是前一项的最大值,所以对于前面 n 个点来说,分别维护一下前面括号中内容的最大值,这样就能 O( 1 ) 去询问最大点对的距离了
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
vector<int>node[4];
int mmax[4];
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n,m;
scanf("%d%d",&n,&m);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
node[0].push_back(x+y);
node[1].push_back(x-y);
node[2].push_back(y-x);
node[3].push_back(-x-y);
}
for(int i=0;i<4;i++)
mmax[i]=*max_element(node[i].begin(),node[i].end());
scanf("%d",&n);
int ans=2e9,mark=-1;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int res=0;
res=max(res,mmax[0]-x-y);
res=max(res,mmax[1]-x+y);
res=max(res,mmax[2]-y+x);
res=max(res,mmax[3]+x+y);
if(res<ans)
{
ans=res;
mark=i;
}
}
printf("%d\n%d\n",ans,mark);
return 0;
}