1. 程式人生 > 實用技巧 >P3964 [TJOI2013]松鼠聚會 切比雪夫距離 轉化 曼哈頓距離

P3964 [TJOI2013]松鼠聚會 切比雪夫距離 轉化 曼哈頓距離

題意:

\(n\)個點的切比雪夫距離和值的最小值

切比雪夫距離:對於\(x_1,y_1,x_2,y_2\)定義兩個點的切比雪夫距離為\(max(|x_1-x_2|,|y_1-y_2|)\)

範圍&性質:\(1\le n\le 10^5,-10^9\le x,y \le 10^9\)

分析:

首先我們先來了解一個小結論:

列舉到\((0,0)\)點曼哈頓距離為1的點:

\((1,0),(0,1),(-1,0),(0,-1),(0.5,0.5),(0.5,-0.5),(-0.5,-0.5),(-0.5,0.5)\)

列舉到\((0,0)\)點切比雪夫距離為1的點:

\((0,1),(1,1),(-1,1),(-1,-1),(0,-1),(1,-1),(1,0),(-1,0)\)

將上述的點畫在座標系裡可以得到兩個正方形,且第二個是第一個向左旋轉\(45^\circ\)後擴充套件2倍得到

由此我們可以推得

將一個點\((x,y)\)的座標變為\((x+y,x−y)\)後,原座標系中的曼哈頓距離 \(=\)新座標系中的切比雪夫距離

將一個點\((x,y)\)的座標變為\((\frac {x+y}2,\frac {x-y}2)\) 後,原座標系中的切比雪夫距離 \(=\) 新座標系中的曼哈頓距離


由以上結論我們可以將給定點的切比雪夫距離轉化為曼哈頓距離,對於每一個點他的座標變化為了\((gx,gy)\)

由於曼哈頓距離有一個優越的性質:可以快速求多個點到一個點的距離和值,所以我們對於新的點進行排序

記列舉的松鼠為\(k\),那麼:

\[ans= \sum_{i=1}^n dis(i,k)=\sum_{i=1}^n[ \;|gx_i-gx_k| \;+\; |gy_i-gy_k|\; ] \]

化簡得到

\[ans=|gx_1-gx_k|+|gx_2-gx_k| \dots + +|gx_n-gx_k|+|gy_1-gy_k|+|gy_2-gy_k|\dots +|gy_n-gy_k| \]

只要維護一下字首和就可以將複雜度降到單次\(\omicron(1)\)

程式碼:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	const int maxn = 1e5+5;
	long long x[maxn],y[maxn],nx[maxn],ny[maxn];
	long long sumx[maxn],sumy[maxn];
	long long ans,n;
	
	long long calc(long long i)
	{
		long long tmpx=lower_bound(nx+1,nx+n+1,x[i])-nx;
		long long tmpy=lower_bound(ny+1,ny+n+1,y[i])-ny;
		return ( (tmpx*x[i]-sumx[tmpx]+sumx[n]-sumx[tmpx]-(n-tmpx)*x[i]) ) + ( tmpy*y[i]-sumy[tmpy]+sumy[n]-sumy[tmpy]-(n-tmpy)*y[i] );
	}
	
	void work()
	{
		scanf("%lld",&n);
		for(long long i=1,a,b;i<=n;i++)
		{
			scanf("%lld%lld",&a,&b);
			x[i]=nx[i]=a+b;
			y[i]=ny[i]=a-b;
		}
		sort(nx+1,nx+n+1);
		sort(ny+1,ny+n+1);
		for(long long i=1;i<=n;i++)
		{
			sumx[i]=sumx[i-1]+nx[i];
			sumy[i]=sumy[i-1]+ny[i];
		}
		
		ans=0x7ffffffffffff;
		for(long long i=1;i<=n;i++)
		{
			ans=min(ans,calc(i));
		}
		printf("%lld",ans>>1);
	}
	
}

int main()
{
	zzc::work();
	return 0;
}