张经理的员工 前缀和
题目链接: https://ac.nowcoder.com/acm/contest/5403/A
本来只是给校赛作为一个简单题,没想到杀疯了orz(通过率只有不到5%

不知道为啥暴力复杂度达到了1e10还有这么多人敢冲
用两个数组分别储存员工数目的前缀和以及员工坐标的前缀和,记为sum1和sum2。
对于一套方案中的两个工位a,b(假设a<b),令mid=(a+b)/2,易知在[1,mid]范围内的员工到a点更近,在[mid+1, 105]范围内的员工到b点更近。
对于要到a的员工,分为不在a的右侧和在a的右侧两类。
拿前往a的点举例:
比如说有k个点在a的左侧,记他们的坐标为x1,x2...xk,他们对答案的贡献就是(a-x1)+(a-x2)+...(a-xk),这个式子可以转化成k*a-(x1+x2+...xk)。
如果说x1,x2...xk在a的右侧(但在mid左侧),他们对答案的贡献就是(x1-a)+(x2-a)+...(xk-a),这个式子就可以转化成(x1+x2+...xk)-k*a
所以用前缀和得到k和(x1+x2+...xk),就可以算出答案。
//计算不在a右侧要到a的员工的答案(即在[1,a]内的员工
ans+=sum1[a]*a-sum2[a];
//计算在a右侧要到a的员工的答案(即在[a+1,mid]内的员工
ans+=(sum2[mid]-sum2[a])-(sum1[mid]-sum1[a])*a
要到b的员工同理,加起来就可以得到答案了。
std:
#include <bits/stdc++.h>
#define N 100000
using namespace std;
typedef long long ll;
ll n, q, ans;
ll sum1[N+10], sum2[N+10];
int main()
{
cin>>n>>q;
for (int i=1, x; i<=n; i++)
{
cin>>x;
sum1[x]++, sum2[x]+=x;
}
for (int i=1; i<=N; i++)
sum1[i]+=sum1[i-1], sum2[i]+=sum2[i-1];
for (int i=1, a, b, mid; i<=q; i++)
{
cin>>a>>b;
if (a>b) swap(a, b);
ans=0, mid=(a+b)/2;
ans+=sum1[a]*a-sum2[a];//[1,a]
ans+=sum2[mid]-sum2[a]-(sum1[mid]-sum1[a])*a;//[a+1,mid]
ans+=(sum1[b]-sum1[mid])*b-(sum2[b]-sum2[mid]);//[mid+1, b]
ans+=sum2[N]-sum2[b]-(sum1[N]-sum1[b])*b;//[b+1, N]
cout<<ans<<"\n";
}
return 0;
}

浙公网安备 33010602011771号