zoukankan      html  css  js  c++  java
  • 整除分块

    整除分块

    前言

    因为最近在学习莫比乌斯反演,发现整除分块这个东西几乎是非常必要的,因为是真的好用,可以把一些需要(O(n))的枚举优化到(O(sqrt n))

    正文

    什么式子可以用整除分块呢?一般是这样

    [sum_{i=1}^nlfloorfrac{n}{i} floor ]

    我们发现(打表或者是自己yy),对于一段连续的区间,(lfloorfrac{n}{i} floor)的值是不变的,那么对于这一段区间,我们就可以跳过,(O(1))计算出这一段区间的值

    既然是分块,那么一块的边界是什么呢?
    首先,明确一点,首端点l是枚举出来的,而末端点是(n/(n/l))(感性理解一下)
    而据dalao分析,时间复杂度是(O(sqrt n)),有了这个范围,我们就可以分块了

    Code

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

    应用

    很多时候,整除分块是配合其他一些函数来用的,such as (mu),(phi)...
    当我们的区间跳跃的时候,函数值也会跳跃,所以就要记得乘上这一段区间的函数值,这个时候就需要前缀和优化了
    听dalao说,有些恶心的题目会卡线性筛(T飞),这个时候就需要杜教筛,你问我杜教筛是什么?问得好,我也不知道(逃~是真的,以后有时间再补吧)

    例题

    放几道整除分块的例题
    [AHOI2005]约数研究
    [CQOI2007]余数求和
    洛谷P3935 Calculating
    第一题没什么思路,直接预处理,在这里放一下代码

    Code

    #include<bits/stdc++.h>
    #define il inline
    #define rg register
    #define lol long long
    #define Min(a,b) (a)<(b)?(a):(b)
    #define Max(a,b) (a)?(b)?(a):(b)
    
    using namespace std;
    
    void in(int &ans) {
        ans=0; char i=getchar();
        while(i<'0' || i>'9') i=getchar();
        while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
    }
    
    int main()
    {
        int n,ans=0; in(n);
        for(rg int l=1,r,len;l<=n;l=r+1) {
            r=n/(n/l),len=r-l+1;
            ans+=n/l*len;
        }
        printf("%d
    ",ans);
    }
    

    第二题也是转换一下题目要求的东西

    [amod b= a-lfloorfrac{a}{b} floor imes b ]

    然后套一下等比数列求和就可以了
    注意一下边界

    Code

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define il inline
    #define rg register
    #define lol long long
    #define Min(a,b) (a)<(b)?(a):(b)
    #define Max(a,b) (a)?(b)?(a):(b)
    
    using namespace std;
    
    void in(lol &ans) {
        ans=0; char i=getchar();
        while(i<'0' || i>'9') i=getchar();
        while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
    }
    
    int main()
    {
        lol n,m;in(n),in(m); lol ans=n*m;
        for(rg lol l=1,r,len;l<=n;l=r+1) {
            if(m/l!=0) r=Min(m/(m/l),n);
            else r=n; len=(r-l+1);
            ans-=(m/l)*len*(l+r)/2;
        }
        printf("%lld
    ",ans);
    }
    

    第三题题解

    下面是配合莫比乌斯反演的题
    [POI2007]ZAP-Queries 题解1
    [SDOI2015]约数个数和 题解2
    YY的GCD 题解3

    博主蒟蒻,随意转载.但必须附上原文链接

    http://www.cnblogs.com/real-l/

  • 相关阅读:
    腾讯为什么会出Q立方浏览器?
    String,StringBuffer与StringBuilder的区别??
    Linux Socket编程(不限Linux)
    将div显示在屏幕正中央
    计算鼠标坐标是否在指定范围内
    正则
    ajax异步通信
    CSS Float 换行
    jQuery强大的jQuery选择器
    给display字段增加筛选功能
  • 原文地址:https://www.cnblogs.com/real-l/p/9630810.html
Copyright © 2011-2022 走看看