張經理的員工
張經理的公司的辦公室長達100000米,從最左端開始每間隔1米都有一個工位(從第1米開始有工位),位於第i米的工位稱爲i號工位,且這些工位都在一條水平線上。他有n個員工,每個員工分別位於xi號工位上(不同員工可能位於同一個工位)。
現在張經理想把員工聚集在某兩個工位上,他有q套方案(每套方案包含兩個工位號,兩個工位號可能相同),他想知道對於每套方案,所有員工都到達兩個工位中的某一個所需走的最短路徑之和是多少。
輸入描述:
第一行輸入兩個正整數n, q
第二行輸入n個正整數xi,分別代表第i個員工的工位
之後q行每行輸入兩個整數a,b,代表該套方案要求的兩個集合位置
(1<=n,q,xi,a,b<=105)(1<=n,q,xi,a,b<=105)
輸出描述:
對於每套方案,輸出一個整數代表答案,每個答案獨佔一行。
示例1
輸入
3 2
1 3 5
1 4
2 1
輸出
2
4
題目來源牛客網
題意
張經理有n個員工,每個員工分別位於xi號工位上(不同員工可能位於同一個工位)。
現在張經理想把員工聚集在某兩個工位上,他有q套方案(每套方案包含兩個工位號,兩個工位號可能相同),他想知道對於每套方案,所有員工都到達兩個工位中的某一個所需走的最短路徑之和是多少?
解題思路
每種方案都給出了a、b兩個聚集工位。在題目中,查詢次數 q 最大爲1e5,所以在每次查詢時間複雜度不能高於log級別,每次查詢給出的a和b。
最短路徑中有三種情況:
(一)、位於 a 左邊的員工應該前往 a 。
(二)、位於 b 右邊的員工應該前往 b 。
(三)、位於a、b之間的工位若相比於到 b 的距離,該工位距離 a 較近則前往 a ,否則前往 b 。
關鍵點:統計出每一個工位上的員工,並提取有用信息。
步驟一、預處理——方便快速查詢出結果
①、數組Y記錄每個工位上的員工人數,用下標來表示不同工位。
②、統計小於等於i位置的員工個數,記錄在數組min_i。
③、統計小於等於i位置的員工下標之和,記錄在數組min_i_sum。
④、統計大於等於i位置的員工個數,記錄在數組max_i。
⑤、統計大於等於i位置的員工下標之和,記錄在數組max_i_sum。
步驟二、計算方式
①、小於等於a工位每個員工的總距離:x = min_i_sum[a] * a - min_i[a]。
②、大於等於b工位每個員工的總距離:y = max_i[b] - max_i_sum[b] * b。
③、若b-a>1,則a與b之間存在工位。
創建變量c=(a+b)/2,劃分更近a與更近b的工位。
a~c員工的最短位移:z = min_i[c] - min_i[a] - (min_i_sum[c] - min_i_sum[a]) * a。
c~b員工的最短位移: w = b * (min_i_sum[b] - min_i_sum[c]) - (min_i[b] - min_i[c])。
(可包含a、b工位員工,因爲它們到自身的距離爲0,不影響結果)
步驟三、得出答案
最終答案:x + y + z + w。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 100005;
ll X[maxn] , Y[maxn];
ll min_i[maxn] , min_i_sum[maxn];
ll max_i[maxn] , max_i_sum[maxn];
int main()
{
int n,q;
cin>>n>>q;
//避免a與b上無工位的情況
for(int i=1;i<=n;i++)
{
scanf("%d",&X[i]);
Y[X[i]]++; //每個工位上的員工數量
}
ll sum = 0,sum2 = 0;
//i距離起點長度
for(int i=1; i<=100000; i++)
{
if(Y[i])
{
sum += i * Y[i];
sum2 += Y[i];
}
//小於等於i位置的員工個數
min_i[i] = sum;
//小於等於i位置的員工下標之和
min_i_sum[i] = sum2;
}
sum = 0,sum2 = 0;
for(int i=100000; i>=1; i--)
{
if(Y[i]){
sum += i * Y[i];
sum2 += Y[i];
}
//大於等於i位置的員工個數
max_i[i] = sum;
//大於等於i位置的員工下標之和
max_i_sum[i] = sum2;
}
while(q--)
{
int a, b;
ll ans = 0;
scanf("%d %d",&a,&b);
if(a > b)
swap(a , b);
ll x, y, z=0, w=0;
//1~a員工的最短位移
x = min_i_sum[a] * a - min_i[a];
//b~n員工的最短位移
y = max_i[b] - max_i_sum[b] * b;
//若a與b之間有工位
if(b - a > 1)
{
int c = (b+a)/2;
//a~c員工的最短位移
z = min_i[c] - min_i[a] - (min_i_sum[c]-min_i_sum[a]) * a;
//c~b-1員工的最短位移
w = b * (min_i_sum[b]-min_i_sum[c]) - (min_i[b]-min_i[c]);
}
cout<<x + y + z + w<<endl;
}
return 0;
}
總結
該題關鍵在於由題目得到更多有用的信息,讓我們能夠通過它們之間的關係快速計算出正確的答案,有效的避免時間複雜度過大。
參考題解
希望能夠將自己的一些學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白