CCF - 202112-2 - 序列查询新解

本文介绍了一种算法,用于处理给定数组A和区间划分,计算在特定区间内函数g(i)与f(i)相等时的点。涉及递推公式推导g(i)的规律,以及如何利用搜索技巧找到f(i)==g(i)的中间位置mid。关键在于理解g(i)的等差性质并结合条件判断进行求和操作。
摘要由CSDN通过智能技术生成

给出一个A数组,A = 【0,2,5,8】,n = 3,N = 10,r = 2;
在样例中可以看出在【0,2),【2,5),【5,8),【8,10)这些区间内,我们处在其中任何一个区间中时,f(i)就是已知的,并且相同。
我们假设一个函数absGfSum(a1, a2, f)表示在【a1,a2)区间内,已知f(i),求g(i)
我们知道在【a1,a2)中,f(i) 是不变的,而g(i)是单调递增的。
所以
1、 当g(a1) >= f 时,表示所有的g(i)都大于等于f(i),直接算出在【a1,a2)中abs( f(i)的总和 - g(i) 的总和);
2、当g(a2) <= f 时,表示所有的g(i)都小于等于f(i),也可以直接算出在【a1,a2)中abs( f(i)的总和 - g(i) 的总和);
3、当以上两个条件不满足时,说明前半段g(i) < f(i) 后半段g(i) >= f(i),那么只要算出中间 g(i) = f(i) 时的位置就行了,假设中间这个位置表示为mid,那么【a1,mid)就是第2种情况,【mid,a2) 就是第一种情况。
综上所述,我们还有两个问题需要解决。
第一个问题:计算【a1,a2)区间内,g(i) 的总和。
我们只需实现一个g(i)前n项和的函数gn,然后gn(a2 - 1)- gn(a1 - 1)即可。
当r = 2时,g(i)是0,0,1,1,2,2,3,3……
当r = 3时,g(i)是0,0,0,1,1,1,2,2,2,3,3,3……
以r = 2为例,将第一项和第二项,第三项和第四项依此类推,相加之后,就形成了一个等差数列。
等差数列an = (首项 + 尾项) * 项数 / 2;
编号:0,1,2,3,4,5,6,7,8……n
g(i) :0,0,1,1,2,2,3,3,4……n / r
相加后:0,2,4,6,8……
通过观察可以发现:g(i) = i / r;
(1)当(i + 1) / r == 0时,说明前 i 项 正好构成 像(0,0,1,1)这样整齐的数列
这时,首项为0, 尾项为 i / r * r (i / r 是g(i),相加后的尾项是 r 个g(i)),
项数为(i + 1)/ r,
(2)当(i + 1)/ r != 0时,我们先算出前i项中 像(0,0,1,1)这样整齐的数列,再加上多出的几项即可。
这时,首项为0,尾项为(i / r - 1) * r,项数为(i + 1) / r,还剩下(n + 1) % r项,所以最后加上(n + 1)% r * (n / r);
第二个问题:找到【a1, a2)区间内f(i) == g(i) 时的位置mid
直接遍历

long long mid = a1;
for(; mid <= a2; mid++){
    if(mid / r == f){
        break;
    }
}

以上就是这道题的解题思路。
需要注意的是数据类型使用long long
下面是我的代码

#include <bits/stdc++.h>
using namespace std;
//计算g(i) 的前n项和
long long gn(long long n, long long r){
    if(n < 0) return 0;
    if((n + 1) % r != 0){
        long long m = (n + 1) % r;
        return (n / r * r - r) * ((n + 1) / r) / 2 + m * (n / r);
    }
    return n / r * r * ((n + 1) / r) / 2;
}
//计算在a1~a2范围内,当f(i)>g(i)时,f(i) - g(i)求和,当f(i)<=g(i)时,g(i) - f(i)求和
long long absGfSum(long long a1, long long a2, long long f, long long r){
    if(a1 / r >= f || a2 / r <= f){
        return abs((a2 - a1) * f - (gn(a2 - 1, r) - gn(a1 - 1, r)));
    }
    long long mid = a1;
    for(; mid <= a2; mid++){
        if(mid / r == f){
            break;
        }
    }
    return (mid - a1) * f - (gn(mid - 1, r) - gn(a1 - 1, r))
     + gn(a2 - 1, r) - gn(mid - 1, r) - (a2 - mid) * f;
}

int main(){
    long long n, N;
    cin >> n >> N;
    long long r = N / (n + 1);
    //记录数组中的前后两个数,数组中第一个数默认是0,所以a1作为数组中的第一个数,输入的数作为a2,
    long long a1 = 0, a2 = 0;
    long long sum = 0;
    for(int j = 0; j < n; j++){
        cin >> a2;
        //j就是[a1, a2)范围内的f(i)的值
        sum += absGfSum(a1, a2, j, r);;
        a1 = a2;
    }
    a2 = N;
    sum += absGfSum(a1, a2, n, r);
    cout << sum << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值