0%

曼哈顿距离与切比雪夫距离

曼哈顿距离转切比雪夫距离

由一道校内模拟赛例题引入

同时这也是出题人偷的牛客的一道题

给定 $n$ 个点的坐标 $(x_i,y_i)$ 选择两个点满足其曼哈顿距离不小于 $d$ ,求方案数

不难画出,离一个点的曼哈顿距离相同的点构成一个菱形:

https://pic.imgdb.cn/item/61922e792ab3f51d912a17f7.png

其满足对于 $(x_O,y_O),(x_i,y_i)$ 有 $\text{abs}(x_O-x_i)+\text{abs}(y_O,y_i)$

一个神奇的 $\text{trick}$ 是将其转化为切比雪夫距离,即,令 $x_i’=x_i+y_i\ ,\ y_i’=x_i-y_i$

不难得出此时的 $(x_O’,y_O’),(x_i’,y_i’)$ 的切比雪夫距离 $\max\left(|x_O’-x_i’|,|y_O’-y_i’|\right)$ 即是原图两点的曼哈顿距离

也就是离一点的曼哈顿距离相同的点构成的图形转化为矩形:

对于这个矩形,树状数组动态维护一下即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const int N=201010;
int n,m,d,ans,l;
struct node{
int x,y;
friend bool operator <(const node &a,const node &b){ return a.y<b.y;}
}a[N];
int c[N<<1];

inline int lowbit(int x){ return x&-x;}
inline void add(int x,int y){ while(x<=l*4) c[x]+=y,x+=lowbit(x);}
inline int sum(int x,int res=0){ while(x>0) res+=c[x],x-=lowbit(x); return res;}

signed main(){
freopen("hole.in","r",stdin); freopen("hole.out","w",stdout);
scanf("%lld%lld%lld",&n,&d,&l);
for(rei i=1,x,y;i<=n;i++) scanf("%lld%lld",&x,&y), a[i].x=x+y,a[i].y=x-y, a[i].x+=2*l+1,a[i].y+=2*l+1;
sort(a+1,a+n+1);
for(rei i=1,j=1;i<=n;++i){
while(j<i && abs(a[i].y-a[j].y)>=d) add(a[j].x,-1),++j;
ans+=sum( min(l*4,a[i].x+d-1) )-sum(max(0ll,a[i].x-d));
add(a[i].x,1);
}
printf("%lld\n",(n*n-n)/2-ans);
getchar(),getchar();
return 0;
}

AT2160 [ARC065C] へんなコンパス / Manhattan Compass

给定 $n$ 个点的坐标 $(x_i,y_i)$ 选择两个点满足其曼哈顿距离等于 $d$ ,求方案数

显然能转成切比雪夫距离,那么点对 $(x_1,y_1),(x_2,y_2)$ 需满足的条件即为:

通过对 $point$ 进行排序后二分查找,不难得到满足上述条件的 $y_l\sim y_r$ 或 $x_l\sim x_r$

考虑如何统计答案,对于合法的点对将两点连边

那么上述条件给定的一个合法区间,该区间内的点两两相连并向 $(x_a,y_a)$ 连边

维护一下每个点的出度,能与初始点 $a$ 连通的即为合法方案 (显然与 $b$ 连通的一定也与 $a$ 连通)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const int N=1e5+100;
PII point[N],sorted_poi[N];
int fa[N],cnt[N],delta[N],id[N];
int x[N],y[N];
int n,a,b,dis;
ll ans;

inline int read(){ rei ans=0; char ch=getchar(); while(ch<'0' || ch>'9') ch=getchar(); while(ch>='0' && ch<='9'){ ans=(ans<<1)+(ans<<3)+(ch^48); ch=getchar();} return ans;}
inline bool cmp(int x,int y){ return point[x]<point[y];}
int get_fa(int x){ return fa[x]==x ? x : fa[x]=get_fa(fa[x]);}
inline void merge(int x,int y){ fa[ get_fa(x) ]=get_fa(y);}
inline void solve(int now){
for(rei i=1;i<=n;++i) id[i]=i;
sort(id+1,id+1+n,cmp);
memset(delta,0,sizeof delta);
for(rei i=1;i<=n;++i) sorted_poi[i]=point[ id[i] ];
for(rei i=1;i<=n;++i){
rei l=lower_bound(sorted_poi+1,sorted_poi+1+n,mk(sorted_poi[i].first+dis,sorted_poi[i].second-dis+now))-sorted_poi;
rei r=upper_bound(sorted_poi+1,sorted_poi+1+n,mk(sorted_poi[i].first+dis,sorted_poi[i].second+dis-now))-sorted_poi-1;
if(l<=r){
++delta[l],--delta[r];
merge(id[i],id[l]);
cnt[ id[i] ]+=r-l+1;
}
}
rei pre=0;
for(rei i=1;i<n;++i){
pre+=delta[i];
if(pre) merge(id[i],id[i+1]);
}
}

int main(){
// freopen("1.in","r",stdin); freopen("1.ans","w",stdout);
n=read(),a=read(),b=read();
for(rei i=1;i<=n;++i) fa[i]=i;
for(rei i=1;i<=n;++i) x[i]=read(),y[i]=read(),point[i].first=x[i]+y[i],point[i].second=x[i]-y[i];
dis=abs(x[a]-x[b])+abs(y[a]-y[b]);
solve(0);
for(rei i=1;i<=n;++i) swap(point[i].first,point[i].second);
solve(1);
for(rei i=1;i<=n;++i) if(get_fa(i)==get_fa(a)) ans+=cnt[i];
printf("%lld\n",ans);
getchar(),getchar();
return 0;
}

切比雪夫距离转曼哈顿距离

P3964 [TJOI2013]松鼠聚会

给定平面上多个点坐标,求其余点到一点的切比雪夫距离之和的最小值

注意,如下定义即为切比雪夫距离: 点 $(x,y)$ 和周围 $8$ 个点 $(x\pm1,0\ ;\ y\pm 1,0 )$ 的距离为 $1$

显然如此转化后点 $(x,y)$ 变为 $\left(\frac{x+y}{2},\frac{x-y}{2}\right)$

枚举中心点 $i$ ,分别讨论横纵坐标对答案的贡献,以横坐标为例

若点 $j$ 横坐标 $x_j$ 满足 $x_j\leq x_i$ ,则其贡献为 $x_i-x_j$ ,否则为 $x_j-x_i$

排序后,若前 $k$ 个的横坐标 $\leq x_i$ ,那么所有横坐标对答案有贡献:

考虑用前缀和维护 $2,3$ 项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const int N=1e5+100;
int n,x[N],y[N],xx[N],yy[N],prex[N],prey[N];
ll ans=1e17;

signed main(){
scanf("%lld",&n);
for(rei i=1,u,v;i<=n;++i) scanf("%lld%lld",&u,&v),x[i]=xx[i]=u+v,y[i]=yy[i]=u-v;
sort(xx+1,xx+1+n); sort(yy+1,yy+1+n);
for(rei i=1,x,y;i<=n;++i) prex[i]=prex[i-1]+xx[i],prey[i]=prey[i-1]+yy[i];
for(rei i=1,k1,k2;i<=n;++i){
k1=lower_bound(xx+1,xx+n+1,x[i])-xx;
k2=lower_bound(yy+1,yy+n+1,y[i])-yy;
ll ansx=(ll) k1*x[i]-2ll*prex[k1]+prex[n]-(ll) (n-k1)*x[i];
ll ansy=(ll) k2*y[i]-2ll*prey[k2]+prey[n]-(ll) (n-k2)*y[i];
ans=min(ans,ansx+ansy);
}
printf("%lld\n",ans/2);
getchar(),getchar();
return 0;
}