牛客前缀和两题-储物点的距离张经理的员工

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,对前缀和是不会造成影响的!

【前缀和处理】
设置两个前缀和数组 sum1sum2
其中,sum1[i] 存放工位小于i的员工的工位号和;sum2[i] 存放工位号小于i的员工数量。

此题如何化简用前缀和得出答案呢?由于有两个点,我们分三种情况:(设为a,b两点为左和右的两点聚集点)
【二分思想】
取所选工位号的中间值 mid=(a+b)/2 ,其中,工位号小于等于mid的向a移动,大于mid的向b移动。

  1. 在a点左边的工位: a ∗ s u m 2 [ a ] − s u m 1 [ a ] ; a*sum2[a]-sum1[a]; asum2[a]sum1[a];

  2. 在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]);

  3. 在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;
  

以上是关于牛客前缀和两题-储物点的距离张经理的员工的主要内容,如果未能解决你的问题,请参考以下文章

B.储物点的距离

P3932 浮游大陆的68号岛

luogu P3932 浮游大陆的68号岛

洛谷 P3932 浮游大陆的68号岛 题解

洛谷10月月赛R2·浴谷八连测R3 -Chtholly-T1

牛客-数学考试——前缀和的k区间问题