BZOJ 1516 [POI2006]Mag-Warehouse 切比雪夫距离转曼哈顿距离

题意:
给定一个网格图,其上有一堆坏点(整点,同一位置多个),求一个整点,使得该整点到所有的坏点的切比雪夫距离之和最小。
求这个整点位置。

无SPJ

解析:
看完题懵了,我只会曼哈顿距离啊怎么办。
然后就无聊查了下给定的那个计算公式,哇塞这居然叫切比雪夫距离。
噫怎么有个链接是谈切比雪夫转化曼哈顿距离的。
噫看完后我就会这道题辣!

对于原坐标系中两点间的 Chebyshev 距离,是将坐标轴顺(逆)时针旋转45度并将所有点的坐标值放大sqrt(2)倍所得到的新坐标系中的Manhattan距离的二分之一。

某点绕原点逆时针旋转α°(或坐标轴顺时针旋转)后,点(x,y)的坐标会变为(cosα*x - sinα*y , sinα*x + cosα*y)。

有了以上两个东西这题就解辣
显然点(x,y)逆时针旋转45度坐标值放大sqrt(2)倍后的坐标是(x-y,x+y)。
于是我们直接把所有点搞过去,分别求x,y的中位数即可。
然后转回来的点有两种:第一坐标都是整数那么直接输出即可。
第二坐标有不是整数的(即.5)我们需要判断floor(x),ceil(x),floor(y),ceil(y)任意组成的点的距离。
这个复杂度O(n)不怂。

你问我没有SPJ这题怎么做?

登陆main.edu.pl,搞到所有数据,求中位数的时候按照他的意思求,取左边或者右边我忘了,答案坐标非整数判断的时候按照他的意思取最终答案(好像是横纵坐标最大)。

赶紧来人写SPJ!

代码:

#include <map>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100100
#define eps 1e-8
using namespace std;
typedef long long ll;
int n;
ll sum;
struct node
{
    ll x,y,t;
    friend istream& operator >> (istream &_,node &a)
    {scanf("%lld%lld%lld",&a.x,&a.y,&a.t);sum+=a.t;return _;}
}pt[N];
map<ll,int>mx,my;
int totx,toty;
struct arr
{
    ll val,num;
}arrx[N],arry[N];
bool cmp(arr a,arr b)
{
    return a.val<b.val;
}
ll sumx[N],sumy[N];
ll xx[5],yy[5];
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("mag.in","r",stdin);
        freopen("mag.out","w",stdout);
    #endif
    scanf("%d",&n);
    for(int i=1;i<=n;i++)cin>>pt[i];
    for(int i=1;i<=n;i++)
    {
        ll x=pt[i].x-pt[i].y,y=pt[i].x+pt[i].y;
        if(!mx[x])
            mx[x]=++totx,arrx[totx].val=x,arrx[totx].num=pt[i].t;
        else arrx[mx[x]].num+=pt[i].t;
        if(!my[y])
            my[y]=++toty,arry[toty].val=y,arry[toty].num=pt[i].t;
        else arry[my[y]].num+=pt[i].t;
    }
    sort(arrx+1,arrx+totx+1,cmp);
    sort(arry+1,arry+toty+1,cmp);
    for(int i=1;i<=totx;i++)sumx[i]=sumx[i-1]+arrx[i].num;
    for(int i=1;i<=toty;i++)sumy[i]=sumy[i-1]+arry[i].num;
    int flag=0;
    if(!(sum&1))flag=1;
    sum>>=1;
    double midX=0,midY=0;
    int l=1,r=totx,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(sumx[mid]<=sum)ans=mid,l=mid+1;
        else r=mid-1;
    }
    if(flag)
    {
        if(sumx[ans]==sum)midX=arrx[ans].val;
        else midX=arrx[ans+1].val;
    }else midX=arrx[ans+1].val;
    l=1,r=toty,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(sumy[mid]<=sum)ans=mid,l=mid+1;
        else r=mid-1;
    }
    if(flag)
    {
        if(sumy[ans]==sum)midY=arry[ans].val;
        else midY=arry[ans+1].val;
    }else midY=arry[ans+1].val;
    double prex=(midX+midY)/2;
    double prey=(midY-midX)/2;
    if(prex-floor(prex)>eps||prey-floor(prey)>eps)
    {
        xx[1]=xx[2]=(ll)floor(prex),xx[3]=xx[4]=(ll)ceil(prex);
        yy[1]=yy[3]=(ll)floor(prey),yy[2]=yy[4]=(ll)ceil(prey);
        ll sum=-1,no;
        for(int i=1;i<=4;i++)
        {
            ll ret=0;
            for(int j=1;j<=n;j++)
            {
                ll tmpx=pt[j].x-xx[i],tmpy=pt[j].y-yy[i];
                if(tmpx<0)tmpx=-tmpx;if(tmpy<0)tmpy=-tmpy;
                ret+=pt[j].t*(max(tmpx,tmpy));
            }
            if(sum==-1||ret<=sum)
            {sum=ret,no=i;}
        }
        printf("%lld %lld\n",xx[no],yy[no]);
    }else
    {
        printf("%lld %lld\n",(ll)prex,(ll)prey);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值