1. 程式人生 > >BZOJ.3170.[TJOI2013]松鼠聚會(切比雪夫距離轉曼哈頓距離)

BZOJ.3170.[TJOI2013]松鼠聚會(切比雪夫距離轉曼哈頓距離)

題目連結

將原座標系每個點的座標\((x,y)\)變為\((x+y,x-y)\),則原座標系中的曼哈頓距離等於新座標系中的切比雪夫距離。
反過來,將原座標系每個點的座標\((x,y)\)變為\((\frac{x+y}{2},\frac{x-y}{2})\),則原座標系中的切比雪夫距離等於新座標系中的曼哈頓距離。
隨便寫兩個點就可以驗證這是對的。

將題目中每個點的座標\((x,y)\)改為\((\frac{x+y}{2},\frac{x-y}{2})\),然後記\(dis(a,b)=\Delta X+\Delta Y\)表示兩個點\(a,b\)間的曼哈頓距離。
列舉每一個點\(x\),則到\(x\)

的距離之和為\(Sum=\sum_{i=1}^ndis(i,x)\)
\(dis(i,x)\)拆開,即\[\begin{aligned}Sum&=\sum_{i=1}^n\Delta X(i,x)+\Delta Y(i,x)\\&=\Delta X(1,x)+\Delta X(2,x)+...+\Delta Y(1,x)+\Delta Y(2,x)...\end{aligned}\]

把每個點按\(x\)\(y\)排序,列舉點時就可以\(O(1)\)計算\(\Delta X\)\(\Delta Y\)的變化量了。
當然沒必要先把座標\(x,y\)除以\(2\)。最後把答案除以\(2\)

即可。

我否認在刷水題的事實。

//3072kb    264ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5;

LL Ans[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Point
{
    int x,y,id;
    Point(int tid=0,int ty=0,int tx=0) {id=tid, x=tx+ty, y=tx-ty;}
}p[N];

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now*f;
}
bool cmpx(Point a,Point b)
{
    return a.x<b.x;
}
bool cmpy(Point a,Point b)
{
    return a.y<b.y;
}

int main()
{
    int n=read();
    for(int i=1; i<=n; ++i) p[i]=Point(i,read(),read());

    std::sort(p+1,p+1+n,cmpx);
    LL sum=-1ll*(n-1)*p[1].x;
    for(int i=2; i<=n; ++i) sum+=p[i].x;
    for(int i=1; i<=n; ++i)
        Ans[p[i].id]+=sum, sum+=(2ll*i-n)*(p[i+1].x-p[i].x);//i*dx-(n-i)*dx

    std::sort(p+1,p+1+n,cmpy);
    sum=-1ll*(n-1)*p[1].y;
    for(int i=2; i<=n; ++i) sum+=p[i].y;
    for(int i=1; i<=n; ++i)
        Ans[p[i].id]+=sum, sum+=(2ll*i-n)*(p[i+1].y-p[i].y);

    LL ans=1e18;
    for(int i=1; i<=n; ++i) ans=std::min(ans,Ans[i]);
    printf("%lld\n",ans>>1);

    return 0;
}