zoukankan      html  css  js  c++  java
  • 数学--数论--整除分块(巨TM详细,学不会,你来打我)

    1.概念
    从一道例题说起
    ni=1nnini=1nni在介绍整除分块之前,我们先来看一道算数题: 已知正整数n,求∑i=1n⌊ni⌋egin{aligned}已知正整数n,求sum_{i=1}^n left⌊dfrac{n}{i} ight⌋end{aligned}

    我们写一个表格看一看1-20的整除是什么样子的

    i 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    20ileft⌊dfrac{20}{i} ight⌋ 20 10 6 5 4 3 2 2 2 2 1 1 1 1 1 1 1 1 1 1

    表中同样的值会连续出现,而相同的值所划分的区间积是整出分块。整除的性质使得从1到n的数组表可根据数值划分为不同的分块,且分块数远远小于n。利用这种性质,我们如果能推导出每个分块具体的左右端点位置在哪,这个问题就可以快速求解出来了。

    2.整除分块公式推导

    向下取整的情形
    还是说我们的例题
    ni=1nni已知正整数n,求sum_{i=1}^n left⌊dfrac{n}{i} ight⌋
     假设我们已知某一个分块的左端点lll,要求解出该分块的右端点rrr。设该分块的数值为kkk,对于该分块中的每个数iii,有k=ni=nlk=left⌊dfrac{n}{i} ight⌋=left⌊dfrac{n}{l} ight⌋,即iknikle n,也就是说我们找到可得使iknikle n成立的最大的i的值即是我们所求的右端点r,因此我们可以得到下列式子:

    {k=nl r=max(i),iknegin{cases}k = left⌊dfrac{n}{l} ight⌋ \space \r = max (i), ik le nend{cases}

    推导可得:

    r=nk=nnlr= left⌊dfrac{n}{k} ight⌋=left⌊dfrac{n}{left⌊dfrac{n}{l} ight⌋ } ight⌋

    容易得到代码:

    ans = 0;
    for(int l = 1, r; l <= n; l = r + 1)
    {
        r = n / (n / l);
        ans += n / l * (r - l + 1);
    }
    

    But 还没有结束

    我们再看这一道题:

    n,a,b,i=1nnai+begin{aligned}已知正整数n, a, b, 求sum_{i=1}^n left⌊dfrac{n}{ai + b} ight⌋end{aligned}

    抓瞎了,但是变变形应该可以做。

    我们记得

    {k=nl r=max(i),iknegin{cases}k = left⌊dfrac{n}{l} ight⌋ \space \r = max (i), ik le nend{cases}
    r=nk=nnlr= left⌊dfrac{n}{k} ight⌋=left⌊dfrac{n}{left⌊dfrac{n}{l} ight⌋ } ight⌋
    以上两个公式,我们有如下
    {k=nal+b r=max(i),(ai+b)knegin{cases}k = left⌊dfrac{n}{al+b} ight⌋ \space \r = max (i), (ai+b)k le nend{cases}

    上式子可推导为


    但是对于这个题目,我们给出第二种推导方式,使得一种推导方式解决多种题目。
    在这里插入图片描述
    这我们再做
    n,i=1nni2egin{aligned}已知正整数n, 求sum_{i=1}^n left⌊dfrac{n}{i^2} ight⌋end{aligned}
    我们按照上面的方式推导
    在这里插入图片描述
    不知道这时候有多少人偷笑,说自己把整除分块学会了,曾经以为自己是个王者,结果他爸来。

    在这里插入图片描述
    这个题你懵了吗,对于一对 L和R,中间的值是等差数列,相差1,归根结底还是整除分块,还是找到l和r,通过等差数列计算即可。
    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        long long n, k, ans = 0;
        long long left = 1, right, rest;
        scanf("%lld%lld", &n, &k);
        while (left <= n && left <= k) //求从1到min(n, k)内的总和
        {
            right = min(k / (k / left), n);	
            rest = k % left;
            ans += (rest + rest - (right - left) * (k / left)) * (right - left + 1) / 2;
            left = right + 1;
        }
        if (n > k)
        {
            ans += k * (n - k);
        }
        cout << ans;
    }
    
    

    完后
    王者你会了吗?
    王者自信满满的说自己会了!

    王者再看看这个题

    ni=1nni已知正整数n,求sum_{i=1}^n left⌈dfrac{n}{i} ight⌉

    没4没4,我们就不推导向上取整了,这里只需要一个小转化,将向上取整转化为向下取整。
    我们考虑没有整除的时候是不是就有
    ni=ni+1left⌈dfrac{n}{i} ight⌉ =left⌊dfrac{n}{i} ight⌋+1 如果整除的时候就相等了,那么我们只要不加1,我们加上i1idfrac{i-1}{i}就可以避免这种情况,那么就可以转化为
    在这里插入图片描述
    通过上面向下取整的推到即可得到
    在这里插入图片描述
    到这里王者就可以独当一面了。

  • 相关阅读:
    UVA 10618 Tango Tango Insurrection
    UVA 10118 Free Candies
    HDU 1024 Max Sum Plus Plus
    POJ 1984 Navigation Nightmare
    CODEVS 3546 矩阵链乘法
    UVA 1625 Color Length
    UVA 1347 Tour
    UVA 437 The Tower of Babylon
    UVA 1622 Robot
    UVA127-"Accordian" Patience(模拟)
  • 原文地址:https://www.cnblogs.com/lunatic-talent/p/12798489.html
Copyright © 2011-2022 走看看