除数分块

虽然学了,但是我还是不知道它为什么叫除数分块。

题目

我觉得这个没有题目不好讲所以就先上题目。
【余数求和】(洛谷OJ P2261)

给出正整数 n 和 k 计算 G(n,k)=k mod 1+k mod 2+k mod 3+⋯+k mod n的值。 其中 k mod i 表示 k 除以i 的余数。
例如:G(10,5)=5 mod 1+5 mod 2+5 mod 3+5 mod 4+5 mod 5⋯+5 mod 10
                    =0+1+2+1+0+5+5+5+5+5=29

解题思路

这一道题乍一看可以直接暴力过,但是有一个严重的问题,n,k的范围数量级都是10^9(上面没说),暴力肯定会超时。所以我们可以怎么做呢?(注:以下的 ÷ \div ÷ / / /都表示数学上的除号,都有使用是为单个式子的美观考虑)

首先,有一个非常有用的转化: k k k m o d mod mod x x x= k − ⌊ k x ⌋ ∗ x k-\left \lfloor \frac{k}{x}\right \rfloor*x kxkx(据说高精取模也用得到)
那么我们就可得到: G ( n , k ) G\left ( n,k \right ) G(n,k)= ∑ x = 1 n ( k − ⌊ k x ⌋ ∗ x ) \sum_{x=1}^{n}(k-\left \lfloor \frac{k}{x}\right \rfloor*x) x=1n(kxkx)= n ∗ k − ∑ x = 1 n ( ⌊ k x ⌋ ∗ x ) n*k-\sum_{x=1}^{n}(\left \lfloor \frac{k}{x}\right \rfloor*x) nkx=1n(xkx)
g ( x ) = ⌊ k ÷ ⌊ k x ⌋ ⌋ g\left ( x \right )=\left \lfloor k\div \left \lfloor \frac{k}{x}\right \rfloor \right \rfloor g(x)=k÷xk, x ∈ [ 1 , n ] x\in \left [ 1,n \right ] x[1,n]
则有 g ( x ) ≥ ⌊ k ÷ ( k x ) ⌋ = x g\left ( x \right )\geq \left \lfloor k\div \left ( \frac{k}{x}\right ) \right \rfloor= x g(x)k÷(xk)=x,证明了 g ( x ) g\left ( x \right ) g(x)不小于x
由此可以推出 ⌊ k / g ( x ) ⌋ ⩽ ⌊ k / x ⌋ \left \lfloor k/g\left ( x \right ) \right \rfloor\leqslant \left \lfloor k/x \right \rfloor k/g(x)k/x
又因为 ⌊ k / g ( x ) ⌋ = ⌊ k / ⌊ k / ⌊ k x ⌋ ⌋ ⌋ ≥ ⌊ k / ( k / ⌊ k x ⌋ ) ⌋ = ⌊ k / k ∗ ⌊ k x ⌋ ⌋ = ⌊ k / x ⌋ \left \lfloor k/g\left ( x \right ) \right \rfloor= \left \lfloor k/\left \lfloor k/\left \lfloor \frac{k}{x} \right \rfloor \right \rfloor \right \rfloor\geq \left \lfloor k/\left ( k/\left \lfloor \frac{k}{x} \right \rfloor \right ) \right \rfloor= \left \lfloor k/k*\left \lfloor \frac{k}{x} \right \rfloor \right \rfloor= \left \lfloor k/x \right \rfloor k/g(x)=k/k/xkk/(k/xk)=k/kxk=k/x
所以,通过以上 ⌊ k / g ( x ) ⌋ ⩽ ⌊ k / x ⌋ \left \lfloor k/g\left ( x \right ) \right \rfloor\leqslant \left \lfloor k/x \right \rfloor k/g(x)k/x ⌊ k / g ( x ) ⌋ ⩾ ⌊ k / x ⌋ \left \lfloor k/g\left ( x \right ) \right \rfloor\geqslant \left \lfloor k/x \right \rfloor k/g(x)k/x两式,我们可以得出 ⌊ k / g ( x ) ⌋ = ⌊ k / x ⌋ \left \lfloor k/g\left ( x \right ) \right \rfloor=\left \lfloor k/x \right \rfloor k/g(x)=k/x
得出这个式子的意义在于,对于 ∀ x ∈ [ x , g ( x ) ] \forall x\in \left [ x,g\left ( x \right ) \right ] x[x,g(x)],都有 ⌊ k / g ( x ) ⌋ = ⌊ k / x ⌋ \left \lfloor k/g\left ( x \right ) \right \rfloor=\left \lfloor k/x \right \rfloor k/g(x)=k/x
这样我们就可以根据 ⌊ k / x ⌋ \left \lfloor k/x \right \rfloor k/x的值把n分成若干块,至于具体是多少块,根据大佬的题解所说 ⌊ k / x ⌋ \left \lfloor k/x \right \rfloor k/x的取值大概有 k \sqrt{k} k 个(具体为什么我作为一个小菜鸡是真的不知道),所以也就大概分成 k \sqrt{k} k 块就行,所以不用担心复杂度的问题。

代码

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
long long n,k,ans,g;
int main()
{
    scanf("%lld%lld",&n,&k);
    ans=n*k;
    for (long long i=1,j;i<=n;i=j+1)//把上面说的x换成i,i和j分别作为左右边界
    {
        g=k/i;
        if (!g)j=n;//当i大于k时直接把右边界跳到最末尾
	else j=min(n,k/g);//n小于等于k时,怕j一下子跳出界了
        ans-=g*(i+j)*(j-i+1)/2; //每个一块中的数都构成等差数列
    } 
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值