数学知识——数论分块

对于一个式子:
∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^{n} \left \lfloor \frac{n}{i} \right \rfloor i=1nin
我们容易发现在某段区间 [ l , r ] [l, r] [l,r], ⌊ n i ⌋ \left \lfloor \frac{n}{i} \right \rfloor in的值是一样的。那么我们如何获取这个区间呢。
我们假设区间左端点为 l l l,同时令 k = ⌊ n l ⌋ k = \left \lfloor \frac{n}{l} \right \rfloor k=ln R ≥ l R \geq l Rl,那么有 n R ≥ k \frac{n}{R} \geq k Rnk

要使得 R R R最大:也就是区间右端点 r r r应该为: ⌊ n k ⌋ \left \lfloor \frac{n}{k} \right \rfloor kn

最终结果应该为: r = ⌊ n ⌊ n l ⌋ ⌋ r = \left \lfloor \frac{n}{\left \lfloor \frac{n}{l} \right \rfloor} \right \rfloor r=lnn

话不多说直接看例题:

一:题目链接:The Fool

题意:

在这里插入图片描述

Sol:

其实打个表能发现,这个式子有规律 这是规律的AC代码

来看看分块怎么解决,我们发现 [ n i ] \left [ \frac{n}{i} \right ] [in]就是 ⌊ n i ⌋ \left \lfloor \frac{n}{i} \right \rfloor in
然后我们就可以利用分块算出这个值。
时间复杂度是: O ( N ) O(\sqrt{N}) O(N )

Code:

	int T;
    cin >> T;
    int tt  = 1;
    while(T -- )
    {
        int n;
        cin >> n;
        int res = 0;
        for(int l = 1, r; l <= n; l = r+ 1)
        {
            if(n / l) r = min(n / (n / l), n); // 保险起见(n/l)不能为0
            else r = n;
            res += (n / l) * (r - l + 1);
        }
        if(res & 1) cout << "Case " << tt ++ << ": odd\n";
        else cout << "Case " << tt ++ << ": even\n";
    }

二.题目链接:Ice Rain

题意:

给出 n n n k k k求出下式的值:
∑ i = 1 n ( k   m o d   i ) \sum_{i=1}^{n} ( k\bmod i) i=1n(kmodi)

Sol:

我们知道: k   m o d   i = k − ⌊ k i ⌋ ∗ i k \bmod i = k - \left \lfloor \frac{k}{i} \right \rfloor * i kmodi=kiki
那么问题转化为: n ∗ k − ∑ i = 1 n ( i ∗ ⌊ k i ⌋ ) n * k - \sum_{i=1}^{n} (i * \left \lfloor \frac{k}{i} \right \rfloor) nki=1n(iik)
对于后面这个式子: ∑ i = 1 n ( i ∗ ⌊ k i ⌋ ) \sum_{i=1}^{n} (i * \left \lfloor \frac{k}{i} \right \rfloor) i=1n(iik)
我们通过分块能发现 ⌊ k i ⌋ \left \lfloor \frac{k}{i} \right \rfloor ik [ l , , r ] [l,,r] [l,,r]区间上值相同,那么这个区间的值就是: ( l + r ) ∗ ( r − l + 1 ) ∗ ⌊ k i ⌋ / 2 (l+r) * (r - l + 1) * \left \lfloor \frac{k}{i} \right \rfloor / 2 (l+r)(rl+1)ik/2
那么问题就迎刃而解了~~

Code:

int n, k;
    while(cin >> n >> k)
    {
        int res = n * k;
        for(int l = 1, r; l <= n; l = r + 1)
        {
            if(k / l ) r = min(k / (k / l), n);
            else r = n;
            res -= (l + r) * (r - l + 1) / 2 * (k / l);
        }
        cout << res << endl;
    }

三. 这是链接. 模积和

题意:

在这里插入图片描述

Sol:

这其实就是一个推式子的题目, 其次他给的模数不是素数!!!所以求逆元要用扩展欧几里得算法来求解

来推式子:
假设 n ≤ m n \le m nm(大于的话交换就好了)
利用容斥可以得到:
∑ i = 1 n ∑ j = 1 m ( n   m o d   i ) ∗ ( m   m o d   j ) − ∑ i = 1 n ( n   m o d   i ) ∗ ( m   m o d   i ) \sum_{i=1}^n \sum_{j=1}^{m}(n \bmod i)*(m \bmod j) - \sum_{i=1}^n (n \bmod i) * (m \bmod i) i=1nj=1m(nmodi)(mmodj)i=1n(nmodi)(mmodi)
同样的我们知道: k   m o d   i = k − ⌊ k i ⌋ ∗ i k \bmod i = k - \left \lfloor \frac{k}{i} \right \rfloor * i kmodi=kiki
所以上式可以写成:
∑ i = 1 n ∑ j = 1 m ( n − i ∗ ⌊ n i ⌋ ) ∗ ( m − j ∗ ⌊ m j ⌋ ) − ∑ i = 1 n ( n m − m i ∗ ⌊ n i ⌋ − n i ∗ ⌊ m i ⌋ + i 2 ∗ ⌊ n i ⌋ ∗ ⌊ m i ⌋ ) \sum_{i=1}^n \sum_{j=1}^{m}(n - i * \left \lfloor \frac{n}{i} \right \rfloor)*(m - j*\left \lfloor \frac{m}{j} \right \rfloor) - \sum_{i=1}^n (nm - mi*\left \lfloor \frac{n}{i} \right \rfloor - ni*\left \lfloor \frac{m}{i} \right \rfloor + i^2 * \left \lfloor \frac{n}{i} \right \rfloor * \left \lfloor \frac{m}{i} \right \rfloor) i=1nj=1m(niin)(mjjm)i=1n(nmmiinniim+i2inim)
稍微化简一下:
∑ i = 1 n ( n − i ∗ ⌊ n i ⌋ ) ∗ ∑ j = 1 m ( m − j ∗ ⌊ m j ⌋ ) − ∑ i = 1 n ( n m − m i ∗ ⌊ n i ⌋ − n i ∗ ⌊ m j ⌋ + i 2 ∗ ⌊ n i ⌋ ∗ ⌊ m i ⌋ ) \sum_{i=1}^n (n - i * \left \lfloor \frac{n}{i} \right \rfloor) * \sum_{j=1}^{m}(m - j*\left \lfloor \frac{m}{j} \right \rfloor) - \sum_{i=1}^n (nm - mi*\left \lfloor \frac{n}{i} \right \rfloor - ni*\left \lfloor \frac{m}{j} \right \rfloor + i^2 * \left \lfloor \frac{n}{i} \right \rfloor * \left \lfloor \frac{m}{i} \right \rfloor) i=1n(niin)j=1m(mjjm)i=1n(nmmiinnijm+i2inim)
最后化简为:
( n 2 − ∑ i = 1 n ( i ∗ ⌊ n i ⌋ ) ∗ ( m 2 − ∑ j = 1 m ( j ∗ ⌊ m j ⌋ ) − ∑ i = 1 n ( n m − m i ∗ ⌊ n i ⌋ − n i ∗ ⌊ m j ⌋ + i 2 ∗ ⌊ n i ⌋ ∗ ⌊ m i ⌋ ) (n^{2}- \sum_{i=1}^n (i * \left \lfloor \frac{n}{i} \right \rfloor) * (m ^{2}-\sum_{j=1}^{m}(j*\left \lfloor \frac{m}{j} \right \rfloor) - \sum_{i=1}^n (nm - mi*\left \lfloor \frac{n}{i} \right \rfloor - ni*\left \lfloor \frac{m}{j} \right \rfloor+ i^2 * \left \lfloor \frac{n}{i} \right \rfloor * \left \lfloor \frac{m}{i} \right \rfloor) (n2i=1n(iin)(m2j=1m(jjm)i=1n(nmmiinnijm+i2inim)

好了,式子推完了,用分块来求可以实现 O ( N ) O(\sqrt N ) O(N )
其中: 1 2 + 2 2 + ⋅ ⋅ ⋅ + n 2 = n ∗ ( n + 1 ) ∗ ( 2 n + 1 ) 6 1^2 + 2^2+···+n^2 = \frac {n*(n+1)*(2n+1)}{6} 12+22++n2=6n(n+1)(2n+1)
由于三个数相乘可能long long都会爆掉,所以要用逆元。另外,此题给的模数,不是一个素数。只有当模数是素数的时候,才可以用快速幂求出 a a a的逆元 a m o d − 2 a^{mod-2} amod2。所以此题要用扩展欧几里得算法求逆元。
另外,注意取模。很容易爆掉()

Code:

#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
using namespace std;

const int mod = 19940417;
const int inv2 = 9970209 ,inv6 = 3323403;
typedef long long LL;

// 1^2 + 2^2 + ··· + x^2
LL sum(int x)
{
    return x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod;
}

signed main(){
    IOS;
    int n, m;
    cin >> n >> m;
    if(n > m) swap(n, m);
    int n1, m1, nm;
    n1 = n * n %  mod;
    int l = 1, r;
    for( ; l <= n; l = r + 1)
    {
        r = n / (n / l);
        n1 = (n1 - ((r - l + 1) * (l + r) % mod * inv2 % mod * (n / l) % mod) + mod) % mod;
    }
    m1 = m * m % mod;
    l = 1;
    for( ; l <= m; l = r + 1)
    {
        r = m / (m / l);
        m1 = (m1 - ((r - l + 1) * (l + r) % mod * inv2 % mod * (m / l) % mod) + mod) % mod;
    }
    l = 1;
    nm = 0;
    for(; l <= n; l = r + 1)
    {
        r = min(n / (n / l), m / (m / l));
        int NM = (r - l + 1) * n % mod * m % mod;
        int MI = (r - l + 1) * (l + r) % mod * inv2 % mod * m % mod * (n / l) % mod;
        int NI = (r - l + 1) * (l + r) % mod * inv2 % mod * n % mod * (m / l) % mod;
        int I2MN = (n / l) * (m / l) % mod * (sum(r) - sum(l - 1) + mod) % mod;
        nm = ((((nm % mod + NM %mod) - MI + mod) % mod - NI + mod) % mod + I2MN + mod) % mod;
    }
    int ans = ((n1 * m1) % mod - nm + mod) % mod;
    cout << ans << endl;
    return 0;
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

W⁡angduoyu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值