张经理的员工 (西工大校赛A题)

链接:https://ac.nowcoder.com/acm/contest/5403/A

张经理的公司的办公室长达100000米,从最左端开始每间隔1米都有一个工位(从第1米开始有工位),位于第i米的工位称为i号工位,且这些工位都在一条水平线上。他有n个员工,每个员工分别位于xi号工位上(不同员工可能位于同一个工位)。

现在张经理想把员工聚集在某两个工位上,他有q套方案(每套方案包含两个工位号,两个工位号可能相同),他想知道对于每套方案,所有员工都到达两个工位中的某一个所需走的最短路径之和是多少。
输入描述:
第一行输入两个正整数n, q

第二行输入n个正整数xi,分别代表第i个员工的工位

之后q行每行输入两个整数a,b,代表该套方案要求的两个集合位置

(1<=n,q,xi,a,b<=10^5)
输出描述:
对于每套方案,输出一个整数代表答案,每个答案独占一行
示例1
输入
复制
3 2
1 3 5
1 4
2 1
输出
复制
2
4
预处理/二分
因为给出的查询次数是1e5,所以时间复杂度不能高于log级别,简述一种O(1)查询的做法(没用二分):因为给出的查询次数是1e5,所以时间复杂度不能高于log级别,简述一种O(1)查询的做法(没用二分):
每次查询给出的a和b,为了使得答案尽可能的小每次查询给出的a和b,为了使得答案尽可能的小
那么a左边的员工应该前往a,b右边的员工应该前往b那么a左边的员工应该前往a,b右边的员工应该前往b
a和b中间的员工,根据距离a和b的距离分情况讨论(其实不需要讨论),接着往下看a和b中间的员工,根据距离a和b的距离分情况讨论(其实不需要讨论),接着往下看
直接根据样例解释吧,容易理解:直接根据样例解释吧,容易理解:
员工位置分别是1,3,5员工位置分别是1,3,5
第一次查询a = 1,b = 4,所以位于1位置的前往1,位于5位置的前往4第一次查询a=1,b=4,所以位于1位置的前往1,位于5位置的前往4
位于a和b中间的位置有2,3,如果每个位置都有人,那位于2位置的一定会前往1,位于3位置的一定会前往4位于a和b中间的位置有2,3,如果每个位置都有人,那位于2位置的一定会前往1,位于3位置的一定会前往4
思路就是这么个思路,怎样预处理呢?思路就是这么个思路,怎样预处理呢?
预处理所有小于等于i位置的员工个数cpre[i], 所有小于等于i位置的员工下标之和pre[i]预处理所有小于等于i位置的员工个数cpre[i],所有小于等于i位置的员工下标之和pre[i]
预处理所有大于等于i位置的员工个数cfpre[i],所有大于等于i位置的员工下标之和fpre[i]预处理所有大于等于i位置的员工个数cfpre[i],所有大于等于i位置的员工下标之和fpre[i]
那么a左边的员工们需要移动的距离 = cpre[a]*a - pre[a]那么a左边的员工们需要移动的距离=cpre[a]∗a−pre[a]
那么b右边的员工们需要移动的距离 = fpre[b] - cfpre[b]*b那么b右边的员工们需要移动的距离=fpre[b]−cfpre[b]∗b
接下来a和b之间的员工们:[a+1,a+(b-a)/2]区间的员工都去a,[a+(b-a)/2 + 1,b-1]区间的员工都去b接下来a和b之间的员工们:[a+1,a+(b−a)/2]区间的员工都去a,[a+(b−a)/2+1,b−1]区间的员工都去b
距离还是直接O(1)处理,你可以理解成在[a,b]中间切一刀,左半部分去a,右半部分去b,细节就不再简述距离还是直接O(1)处理,你可以理解成在[a,b]中间切一刀,左半部分去a,右半部分去b,细节就不再简述
代码

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<queue>
#include <map>
using namespace std;
typedef long long ll;
const ll maxn=1e5+10;
ll a,b[maxn];
ll low[maxn],clow[maxn];
ll high[maxn],chigh[maxn];
int main()
{
	ll n,m,i,j,k;
	cin >> n >> m;
	memset(b,0,sizeof(b));
	for(i=0;i<n;i++)
    {
        scanf("%lld",&a);
        b[a]++;
    }
    ll sum=0,sum2=0;
    for(i=1;i<=100000;i++)
    {
        if(b[i])
        {
            sum+=b[i];
            sum2+=b[i]*i;
        }
        low[i]=sum;
        clow[i]=sum2;
    }
    sum=0;
    sum2=0;
    for(i=100000;i>=1;i--)
    {
        if(b[i])
        {
            sum+=b[i];
            sum2+=b[i]*i;
        }
        high[i]=sum;
        chigh[i]=sum2;
    }
    ll x,y;

    for(i=0;i<m;i++)
    {
         sum=0;
        cin>> x >> y;
        if(x>y)
        {
            ll t;
            t=x;
            x=y;
            y=t;
        }
        sum+=low[x]*x-clow[x];
        sum+=chigh[y]-high[y]*y;
        if(y-x>1)
        {
            k=y-x;
            ll mid=x+k/2;
            sum+=clow[mid]-clow[x]-(low[mid]-low[x])*x;
            sum+=(low[y-1]-low[mid])*y-(clow[y-1]-clow[mid]);
        }
         cout << sum << endl;

    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值