【2018/10/11測試T2】棋盤問題
【題目】
題目描述:
小 O 對國際象棋有著濃厚的興趣,因為他水平高超,每次人機對戰他總是輕鬆獲勝,所 以他決定自己跟自己下國際象棋。
小 O 的棋盤非常大,達到了 ,現在他在棋盤上擺放了 個國王,並對你提出了 次詢問,每次詢問指定一個座標,問將所有國王從初始位置全部移動到這個座標所需要的最小步數是多少,詢問之間相互獨立,也就是說每次詢問結束後國王會全部回到原來位置。
注意:由於小 O 擔心大家無法理解過於高深的規則,所以在本題中,國王之間不會發生相互攻擊而且多個國王可以同時處在一個格子中, 國際象棋中國王一步只能移動到與其八連通的格子中。
輸入格式:
第一行一個正整數 T 表示資料組數。 對於每組資料,共有 行: 第一行兩個數字 分別表示國王數量和詢問數量。 接下來 行,每行兩個數字 表示國王所在座標。 接下來 行,每行兩個數字 表示目標座標。
輸出格式:
對於每組資料,輸出共有 行,每行一個整數表示對應詢問的答案。
樣例資料:
輸入 1 1 1 233 666 666 233
輸出 433
備註:
【資料範圍】 本題共 7 個測試點,不採用 subtask
資料範圍中的 範圍表示 的範圍,未標註即為沒有特殊限制
對於全部資料,滿足 的總和不超過 且 的總和不超過 ,輸入檔案中所有數字均為正整數且不超過 .
#:,滿足 ; #:,滿足 ,,,; #:,滿足 ,,; #:,滿足 ,,; #:,滿足所有資料中 的總和不超過 ; #:,滿足 ,; #:,無特殊限制。
【分析】
前面三個測試點較為簡單,就不說了,直接從後面開始。
第 4、5 個測試點:
注意到國王從 (x,y) 移動到 (a,b) 所需步數為 Max(|x-a|,|y-b|)。
充分性:可以斜向移動 Min(|x-a|,|y-b|) 次,然後走直線即可到達目標,此時花費次數即為 Max(|x-a|,|y-b|) 步。
必要性:每次移動只能使橫縱座標變化至多 1,所以至少要移動 Max(|x-a|,|y-b|) 次 。
於是對於每個詢問暴力計算出這個值累加求和即可(注意資料比較大,要開 long long)。
第 6、7 個測試點:
注意到 Max(|x-a|,|y-b|) 實際上求的是兩點之間的切比雪夫距離。
有 Max 不好求解,這裡就有一個重要的技巧:將切比雪夫距離轉化為曼哈頓距離。
問題轉化為給定 N 個點 (Kxi,Kyi),Q 組詢問 (Txi,Tyi),求每個詢問的點到這 N 個點的曼哈頓距離和。
注意到曼哈頓距離中 x,y 座標對答案的貢獻相互獨立,所以可以分開計算貢獻。
於是問題又可以進一步簡化:給定 N 個數 Kxi,Q 次詢問 Txi,求每個詢問的數與這 N 個數的絕對值之和。
對於 Kxi<Txi,對答案的貢獻為Txi-Kxi;對於 Kxi>=Txi,對答案的貢獻為 Kxi-Txi。
於是將 Kxi 排序後預處理出字首和, 查詢時直接在有序陣列上二分找出分界點, 利用字首和 O(1) 計算出答案。
Kyi 的處理方法同 Kxi 這裡就不再贅述
也可以參考一下我的另一篇部落格戳這裡
【程式碼】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
#define inf 1ll<<50ll
using namespace std;
int x[N],y[N];
long long sumx[N],sumy[N];
int main()
{
// freopen("chess.in","r",stdin);
// freopen("chess.out","w",stdout);
int n,q,i,t,xx,yy;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&q);
for(i=1;i<=n;++i)
{
scanf("%d%d",&xx,&yy);
x[i]=xx+yy,y[i]=xx-yy;
}
sort(x+1,x+n+1);
sort(y+1,y+n+1);
for(i=1;i<=n;++i)
{
sumx[i]=sumx[i-1]+x[i];
sumy[i]=sumy[i-1]+y[i];
}
long long res;
for(i=1;i<=q;++i)
{
scanf("%d%d",&xx,&yy);
int tx=xx+yy,ty=xx-yy;
int px=lower_bound(x+1,x+n+1,tx)-x-1;
int py=lower_bound(y+1,y+n+1,ty)-y-1;
res=1ll*px*tx-sumx[px]+(sumx[n]-sumx[px])-1ll*(n-px)*tx;
res+=1ll*py*ty-sumy[py]+(sumy[n]-sumy[py])-1ll*(n-py)*ty;
printf("%lld\n",res/2);
}
}
// fclose(stdin);
// fclose(stdout);
return 0;
}