[TJOI2013] 松鼠聚会题解

Tag : 曼哈顿距离 , 切比雪夫距离

[TJOI2013]松鼠聚会

  • 对于一个点,一个点到周围的八个点距离为 1,即为 切比雪夫距离 的定义距离

  • 易得两点之间距离为 m a x ( ∣ x 2 − x 1 ∣ , ∣ y 2 − y 1 ∣ ) max(|x2-x1|,|y2-y1|) max(x2x1∣,y2y1∣)

  • 曼哈顿距离 ∣ x 2 − x 1 ∣ + ∣ y 2 − y 1 ∣ |x2-x1|+|y2-y1| x2x1∣+y2y1∣

  • 两者之间可以相互转换

  • 此题要我们求一些点到点的 切比雪夫距离 之和,带有 m a x max max 的加减不是很好做,化为 曼哈顿距离 ,即化为绝对值的加减会好做许多


  • 切比雪夫距离 转换为 曼哈顿距离

  • 切比雪夫距离 下距离原点 1个单位的点会形成一个如图所示的正方形

  • 曼哈顿距离 下距离原点1个单位的点会形成一个如图所示的倾斜的正方形在这里插入图片描述

  • 转移公式:

  • 切比雪夫曼哈顿

    ( x , y ) → ( x + y 2 , x − y 2 ) (x,y)\to(\frac{x+y}{2},\frac{x-y}{2}) (x,y)(2x+y,2xy)

  • 曼哈顿切比雪夫

    ( x , y ) → ( x + y , x − y ) (x,y)\to(x+y,x-y) (x,y)(x+y,xy)


  • 回到题目,我们应该先将坐标按上文所述的公式转化为在 曼哈顿距离 下的坐标

  • 假设聚会地点为 i i i 号点

  • 最终答案即为 ∣ x 1 − x i ∣ + . . . + ∣ x i − x i ∣ + . . . + ∣ x n − x i ∣ |x_1-x_i|+...+|x_i-x_i|+...+|x_n-x_i| x1xi+...+xixi+...+xnxi
    加上 ∣ y 1 − y i ∣ + . . . + ∣ y i − y i ∣ + . . . + ∣ y n − y i ∣ |y_1-y_i|+...+|y_i-y_i|+...+|y_n-y_i| y1yi+...+yiyi+...+ynyi

  • 如果我们按序列 x x x 排序,那么对于前 i i i 项, x 1 . . . x i ≤ x i x_1...x_i\leq x_i x1...xixi ,
    和即为 i ∗ x i − ( x 1 + . . . + x i ) i*x_i-(x_1+...+x_i) ixi(x1+...+xi) ,用前缀和表示即为 i ∗ x i − s u m i i*x_i-sum_i ixisumi

    而对于第 i + 1 i+1 i+1 n n n 个数,和即为 ( x i + 1 + . . . + x n ) − ( n − i ) ∗ x i (x_{i+1}+...+x_n)-(n-i)*x_i (xi+1+...+xn)(ni)xi,用前缀和表示即为 s u m n − s u m i − ( n − i ) ∗ x i sum_n-sum_i-(n-i)*x_i sumnsumi(ni)xi

  • 另一方面,我们将序列按 y y y 排序,二分找出 x i x_i xi 对应的 y i y_i yi 在按 y y y 排序后的位置 i i i ,同理加上 i ∗ y i − s u m i i*y_i-sum_i iyisumi 以及 s u m n − s u m i − ( n − i ) ∗ y i sum_n-sum_i-(n-i)*y_i sumnsumi(ni)yi

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n;
struct Node{
	double x,y;
}a[MAXN],b[MAXN],c[MAXN],pre[MAXN];
void build()
{
	for(int i=1;i<=n;i++)
	{
		b[i].x=(a[i].x+a[i].y)/2;
		b[i].y=(a[i].x-a[i].y)/2;
		c[i] = b[i];
	}
}
bool cmp1(Node p,Node q){return p.x<q.x;}
bool cmp2(Node p,Node q){return p.y<q.y;}
void Pre()
{
	for(int i=1;i<=n;i++)
	{
		pre[i].x = pre[i-1].x + b[i].x;
		pre[i].y = pre[i-1].y + c[i].y;
	}
}
int search(double target)
{
	int l=1,mid,r=n;
	while(l<=r-5)
	{
		mid=(l+r)>>1;
		if(c[mid].y <= target) l=mid;
		else r=mid;
	}
	for(int i=l;i<=r;i++)
		if(c[i].y==target) return i;
}
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i].x>>a[i].y;
	
	build();
	
	sort(b+1,b+n+1,cmp1);
	sort(c+1,c+n+1,cmp2);
	
	Pre();

	double ans=1e18+7;
	for(int i=1;i<=n;i++)
	{
		double res=0;
		res += b[i].x * i - pre[i].x;
		res += (pre[n].x - pre[i].x) - b[i].x * (n-i);
		
		int pos = search (b[i].y);
		res += c[pos].y * pos - pre[pos].y;
		res += (pre[n].y - pre[pos].y) - c[pos].y * (n-pos);
		ans = min(ans,res);
	}
	
	cout<<(int)ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值