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}就可以避免这种情况,那么就可以转化为
    在这里插入图片描述
    通过上面向下取整的推到即可得到
    在这里插入图片描述
    到这里王者就可以独当一面了。

  • 相关阅读:
    [LeetCode] Lowest Common Ancestor of a Binary Search Tree
    [LeetCode] Palindrome Linked List
    Android控件开发之Chronometer(转)
    andriod 动态设置TextView 和 RelativeLayou 高度
    android RelativeLayout 动态设置高度
    android 判断字符串是否为空与比对["=="与equals()的区别]
    android 实现ImageView按压效果和解决背景图片拉申问题
    android XML布局 属性与运用
    android 解决.XML提示ava.lang.NullPointerException at错误后XML没显示
    Android设置AlertDialog点击按钮对话框不关闭(转)
  • 原文地址:https://www.cnblogs.com/lunatic-talent/p/12798489.html
Copyright © 2011-2022 走看看