NC655 牛牛算题

题目陈述

大意:给定一个正整数 n n n,充当被除数。对于 p , 1 ≤ p ≤ n p,1 \leq p \leq n p,1pn,充当除数,求所有 p p p的商与余数的乘积之和,即 ∑ k ∗ m \sum k*m km,答案取模 m o d mod mod

算法一:朴素算法

算法思路

  • 根据题意,一个很显然的算法,就是对于每一个 p p p分别计算出他的 k , m k,m k,m然后计算 k ∗ m k*m km

代码实现

class Solution {
public:
    long long cowModCount(long long n) {
        long long ans = 0, mod = 1e9 + 7, k, m;
        for (int p = 1; p <= n; p ++ ) //枚举所有合法的p
        {
            k = n / p; //求解商
            m = n % p; //求解余数
            ans = (ans + k * m % mod) % mod; //将他们的贡献加入到答案中
        }
        return ans;
    }
};

复杂度分析

  • 时间复杂度,每个合法的 p p p都遍历了一遍,即为 p p p的区间大小,时间复杂度为 O ( n ) O(n) O(n),当然 n m a x = I N T _ M A X n_{max}=INT\_MAX nmax=INT_MAX,显然还是会 T L E TLE TLE
  • 空间复杂度,定义了四个变量 a n s , m o d , k , m ans,mod,k,m ans,mod,k,m,为 O ( 1 ) O(1) O(1)

算法二:数学推导+整除分块

前置知识:整除分块

  • 此处我们我们需要先了解一个前置知识:整除分块

  • 首先,要了解整除分块是干什么的?设计整除分块的题目大概都具有以下形式 ∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n \lfloor \cfrac{n}{i}\rfloor i=1nin

  • 上面的括号代表向下取整,即整除

  • 我们以 n = 6 n=6 n=6为例子
    在这里插入图片描述
    在这里插入图片描述

  • 我们可以发现对于一个 i i i,往往是一段区间的整除值都是同一个值(废话)

  • 假如我们通常知道了区间内的一个下标(一般是左端点),如何求得跟它所在区间的右端点

  • n = i ∗ j + r , r ∈ [ 0 , i − 1 ] n=i*j+r,r\in [0,i-1] n=ij+rr[0,i1],显然我们可以这样表示 i i i,而把 j j j设置为倍数

  • 对于右端点 r r r,必然有 r < j r<j r<j

  • 反证法,若 r > = j r>=j r>=j则,可以令 r ′ = r − j r'=r-j r=rj, n = r ∗ j + r = ( r + 1 ) ∗ j + r ′ n=r*j+r=(r+1)*j+r' n=rj+r=(r+1)j+r

  • 就得到了区间内比右端 r r r还靠右的满足条件的点

  • 所以:对于右端点 r r r,必然有 r < j r<j r<j

  • 我们就可以根据这个性质,通过 l l l,求得 j j j,再通过 j j j求得 r r r

  • 故对于一个区间,我们知道左端点 l l l,即可以通过公式计算右端点 r = ⌊ n ⌊ n l ⌋ ⌋ r = \lfloor \cfrac{n}{\lfloor \cfrac{n}{l}\rfloor}\rfloor r=lnn,代码中写作r = n / (n / i),注意此处为整除

  • 时间复杂度为 O ( n ) O(\sqrt n) O(n )

算法思路

  • 显然这是一道数学题,我们考虑数学方法,当然,也少不了推导和化简
  • 对于所有的 p , ( p ∈ [ 1 , n ] ) p,(p\in[1,n]) p,(p[1,n])
  • a n s = ∑ ⌊ n p ⌋ × ( n % p ) ans =\sum \lfloor \cfrac{n}{p}\rfloor \times (n \% p) ans=pn×(n%p)

  • a n s = ∑ p = 1 n ⌊ n p ⌋ × ( n − p × ⌊ n p ⌋ ) ans=\sum_{p=1}^{n} \lfloor \cfrac{n}{p}\rfloor \times (n - p \times \lfloor \cfrac{n}{p}\rfloor) ans=p=1npn×(np×pn)
  • 整除分块思想有, k ∈ [ l , r ] k \in[l,r] k[l,r],其中 r = ⌊ n ⌊ n l ⌋ ⌋ r = \lfloor \cfrac{n}{\lfloor \cfrac{n}{l}\rfloor}\rfloor r=lnn
  • 同一块 ⌊ n k ⌋ \lfloor \cfrac{n}{k}\rfloor kn都相同,即 ⌊ n k ⌋ = ⌊ n l ⌋ \lfloor \cfrac{n}{k}\rfloor=\lfloor \cfrac{n}{l}\rfloor kn=ln
  • 故对于同一块有 ∑ k = l r ⌊ n l ⌋ × ( n − k × ⌊ n l ⌋ ) \sum_{k=l}^{r} \lfloor \cfrac{n}{l}\rfloor \times (n-k \times \lfloor \cfrac{n}{l}\rfloor) k=lrln×(nk×ln)
  • 展开括号得
    ∑ k = l r ( n × ⌊ n l ⌋ − k × ⌊ n l ⌋ 2 ) \sum_{k=l}^{r}(n\times \lfloor \cfrac{n}{l}\rfloor-k\times\lfloor \cfrac{n}{l}\rfloor ^2) k=lr(n×lnk×ln2)
  • 求和符号性质有
    ∑ k = l r n × ⌊ n l ⌋ − ∑ k = l r k × ⌊ n l ⌋ 2 \sum_{k=l}^{r}n\times \lfloor \cfrac{n}{l}\rfloor -\sum_{k=l}^{r}k\times \lfloor \cfrac{n}{l}\rfloor^2 k=lrn×lnk=lrk×ln2
  • l e n = r − l + 1 len=r-l+1 len=rl+1
  • 化简求和结果,等差数列公式有
    l e n × n × ⌊ n l ⌋ − ( l + r ) × l e n 2 × k × ⌊ n l ⌋ 2 len\times n \times \lfloor \cfrac{n}{l}\rfloor - \cfrac{(l+r)\times len}{2}\times k \times \lfloor \cfrac{n}{l}\rfloor^2 len×n×ln2(l+r)×len×k×ln2
  • 现在已知每一块的 O ( 1 ) O(1) O(1)求法
  • 整除分块得到了每一块的答案即可求得 a n s ans ans
  • 下面是我的手写推导过程
    在这里插入图片描述

关于不用逆元

  • 肯定有同学好奇为什么我代码里面除了 2 2 2,但是没有用逆元?
  • 因为那一整项求和公式,必然能整除2,故不需要逆元
  • 证明如下:
  • 只要 ( l + r ) (l+r) (l+r) l e n len len中有一个为偶数,就可以整除2
  • l , r l,r l,r奇偶性相同时候, ( l + r ) (l+r) (l+r)为偶数, l e n = r − l + 1 len=r-l+1 len=rl+1为奇数,能整除
  • l , r l,r l,r奇偶性不同的时候, ( l + r ) (l+r) (l+r)为奇数, l e n = r − l + 1 len=r-l+1 len=rl+1为偶数,能整除
  • 故此处不需要用到逆元

复杂度分析

  • 时间复杂度,整除分块为 O ( n ) O(\sqrt n) O(n ),对于每一块表达式求值为 O ( 1 ) O(1) O(1),总得时间复杂度为 O ( n ) O(\sqrt n) O(n )
  • 空间复杂度,定义了几个变量,为 O ( 1 ) O(1) O(1)

代码实现

class Solution {
public:
    long long cowModCount(long long n) {
        long long ans = 0, mod = 1e9 + 7;
        for (long long l = 1, r, len, tmp; l <= n; l = r + 1)
        {
            tmp = n / l;//重复计算所以用tmp暂存
            r = n / tmp;//根据公式计算右边界
            len = r - l + 1;//计算区间长度
            ans = (ans + len * tmp * n % mod - tmp * tmp % mod * (l + r) * len / 2 % mod) % mod;
        }//根据我们上面计算的公式,累加到ans值上面
        if (ans < 0)
            ans += mod;//C++里面负数取模依旧为负数,需要加上mod
        return ans;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值