牛客前缀和两题-储物点的距离张经理的员工
Posted C+++++++++++++++++++
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客前缀和两题-储物点的距离张经理的员工相关的知识,希望对你有一定的参考价值。
文章目录
题目一:储物点的距离
题目详解
- 这是一个前缀和分解过程的问题。由于结果是要取模的!所以代码里面没有一个%mod是无辜的,少一个就直接出错!
具体的操作过程如下:
解题代码
#include "bits/stdc++.h"
#define MAXN 200005
using namespace std;
using ll = long long;
const int mod = 1000000007;
int n, m;
ll a[MAXN]; //TODO 物品距离的前缀和,用于迅速求解 i 到 j 之间的距离
ll b[MAXN]; //TODO 物品数量的前缀和,用于迅速求解[l,r]之间的物品数量
ll c[MAXN]; //TODO 物品数量*对应距离的前缀和,用于迅速求解[l,r]之间所有物品数量乘以对应的距离
//TODO 注意取模的时候,有减法!!要+mod再取
int main()
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
ll d, c;
for (int i = 2; i <= n; i++)
cin >> d;
a[i] = (a[i - 1] + d) % mod;
for (int i = 1; i <= n; i++)
cin >> c;
b[i] = (b[i - 1] + c) % mod;
::c[i] = (::c[i - 1] + a[i] * c) % mod;
ll x, l, r, ret;
//TODO 注意:取模中出现减号,为了防止负数产生,一定要+mod再取模!不信你去掉一个mod试试,保证全扑街!
// 下面的::c表示用的全局范围内的c,而不是上面定义的局部变量
for (int i = 0; i < m; i++)
cin >> x >> l >> r;
if (x >= r)
ret = (((a[x] * (b[r] - b[l - 1])) % mod - ((::c[r] - ::c[l - 1]) % mod + mod) % mod) % mod + mod) % mod;
else if (x <= l)
ret = (((::c[r] - ::c[l - 1]) % mod + mod) % mod - (a[x] * (b[r] - b[l - 1])) % mod + mod) % mod;
else
ret = (((a[x] * (b[x] - b[l - 1])) % mod - (::c[x] - ::c[l - 1]) % mod + mod) % mod + mod) % mod;
ret = (ret + (((::c[r] - ::c[x]) % mod + mod) % mod - (a[x] * (b[r] - b[x])) % mod + mod)) % mod;
cout << ret << '\\n';
return 0;
题目二:张经理的员工
题目详解
- 对于前缀和数组的处理和上题是一模一样,但最终得出答案的运用方法却又些不同!
同样,和上一题的思考方式相同,我们需要先列出它最基本的等式,然后根据这个等式,我们来进行化简,最终将答案以前缀和的方式展示出来。注意:虽然本题给出的是每个人的工位位置,但实际我们只需要一个数组进行存储某个位置有多少人,而有些位置是没有工位的,所以是0,对前缀和是不会造成影响的!
【前缀和处理】
设置两个前缀和数组 sum1
和 sum2
。
其中,sum1[i]
存放工位小于i的员工的工位号和;sum2[i]
存放工位号小于i的员工数量。
此题如何化简用前缀和得出答案呢?由于有两个点,我们分三种情况:(设为a,b两点为左和右的两点聚集点)
【二分思想】
取所选工位号的中间值 mid=(a+b)/2
,其中,工位号小于等于mid的向a移动,大于mid的向b移动。
-
在a点左边的工位: a ∗ s u m 2 [ a ] − s u m 1 [ a ] ; a*sum2[a]-sum1[a]; a∗sum2[a]−sum1[a];
-
在a和b之间的工位
当位于a右边(a~mid):
( s u m 1 [ m i d ] − s u m 1 [ a ] ) − ( s u m 2 [ m i d ] − s u m 2 [ a ] ) ∗ a (sum1[mid]-sum1[a])-(sum2[mid]-sum2[a])*a (sum1[mid]−sum1[a])−(sum2[mid]−sum2[a])∗a
当位于b的左边(mid~b):
( s u m 2 [ b ] − s u m 2 [ m i d ] ) ∗ b − ( s u m 1 [ b ] − s u m 1 [ m i d ] ) ; (sum2[b]-sum2[mid])*b-(sum1[b]-sum1[mid]); (sum2[b]−sum2[mid])∗b−(sum1[b]−sum1[mid]); -
在b右边的工位: s u m 1 [ M A X N ] − s u m 1 [ b ] − ( s u m 2 [ M A X N ] − s u m 2 [ b ] ) ∗ b ; sum1[MAXN]-sum1[b]-(sum2[MAXN]-sum2[b])*b; sum1[MAXN]−sum1[b]−(sum2[MAXN]−sum2[b])∗b;
解题代码
#include<stdio.h>
#define MAXN 100000
using ll = long long;
int site[MAXN+1];//工位号与当前下标相同的员工数
int sum1[MAXN+1];//小于当前下标的工位号之和
int sum2[MAXN+1];//工位号小于当前下标的人数
int main()
int n,q;
scanf("%d%d",&n,&q);
//桶排序方式输入
for(int i=0;i<n;i++)
int temp;
scanf("%d",&temp);
site[temp]++;
//前缀和处理
for(int i=1;i<=MAXN;i++)
sum1[i]=sum1[i-1]+site[i]*(i);
sum2[i]=sum2[i-1]+site[i];
for(int i=1;i<=q;i++)
int a,b;
scanf("%d%d",&a,&b);
//二分法,所选工位的中间值
//TODO 以中间为界限,方便分区计算正负
int mid=(a+b)/2;
ll ans=0;
//保证a<=b
if(a>b)
int t=a;
a=b;
b=t;
//a左边(1~a)
ans+=a*sum2[a]-sum1[a];
//a右边(a~mid)
ans+=(sum1[mid]-sum1[a])-(sum2[mid]-sum2[a])*a;
//b左边(mid+1~b)
ans+=(sum2[b]-sum2[mid])*b-(sum1[b]-sum1[mid]);
//b右边(b~MAX)
ans+=sum1[MAXN]-sum1[b]-(sum2[MAXN]-sum2[b])*b;
printf("%d\\n",ans);
return 0;
以上是关于牛客前缀和两题-储物点的距离张经理的员工的主要内容,如果未能解决你的问题,请参考以下文章