张经理的员工
张经理的公司的办公室长达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;
}
总结
该题关键在于由题目得到更多有用的信息,让我们能够通过它们之间的关系快速计算出正确的答案,有效的避免时间复杂度过大。
参考题解
希望能够将自己的一些学习经验分享给有需要的人。
我是小郑,一个坚持不懈的小白