牛客网题集——张经理的员工(数学逻辑思维)

张经理的员工

张经理的公司的办公室长达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;
}

总结

该题关键在于由题目得到更多有用的信息,让我们能够通过它们之间的关系快速计算出正确的答案,有效的避免时间复杂度过大。
参考题解

希望能够将自己的一些学习经验分享给有需要的人。
我是小郑,一个坚持不懈的小白

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章