浅谈整除分块

整除分块

HGOI20181027模拟题:

给定n,m,快速求出 FHT(n,m)的值。

 对于100%的数据: n,m <= 1,000,000,000

然后为了这个问题我们去找了整除分块的算法:

(例题) [CQOI2007]余数求和

        

https://www.luogu.org/problemnew/show/P2261

 解析:

对于G(n,k) 化简 显然 a%b=a-[a/b]*b

 

在C++中floor(a/b)=a/b 所以我们打个表来看看k/i的规律

# include<bits/stdc++.h>
using namespace std;
int main()
{
    int n=10;
    printf("i      ");
    for (int i=1;i<=n;i++)
       printf("%d ",i);
    putchar('\n'); printf("%d/i",n); 
    for (int i=1;i<=n;i++) 
     printf("%d ",n/i);
    putchar('\n');
    return 0;
} 

我们发现这样的规律:n/i的值相同的总是出现在相邻位置!

 

在上面的式子中也就是说k/i这个东西是下取整的在一个区间内是不会变得

设[k/i]=a (这里是下取整[]): a<=k/i<a+1

然后参变分离:

得 :      i<=k/a;     i>k/(a+1)

所以在这里我们发现了对于区间i∈[L,R]其[k/i]是一个定值,当且仅当L=k/([k/i]+1)+1 ; R=k/[k/i],

所以这里的区间[l,r]可以写成: [k/(k/i+1)+1,k/(k/i) ] (这里的/是整除,和C++中/等价)

对于这里区间他们的floor(k/i)是一定的,唯一的是i在+1 +2 +3的递增所以是一个d=1的等差数列(起始值是k/i),我们只要知道左右区间就可以知道求和以后的值了

  • code:(100pts)
# include <bits/stdc++.h>
# define int long long
using namespace std;
int n,k;
int calc(int n,int k) {
    int ret=0;
    for (int l=1,r;l<=n;l=r+1) {
        if (k/l!=0) r=min(k/(k/l),n);
        else r=n;
        ret+=(k/l)*(r-l+1)*(l+r)/2;
    }
    return ret;
}
int G(int n,int k) { return n*k-calc(n,k);}
signed main()
{
    scanf("%lld%lld",&n,&k);
    printf("%lld\n",G(n,k));
    return 0;
}

HGOI20181027模拟题:

给定n,m,快速求出 FHT(n,m)的值。

 对于100%的数据: n,m <= 1,000,000,000

先对式子化简:

然后就是求一部分就行了,然后就是和上面处理方法一样令G(n,k)中n=k就可以解决了

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int mo=1e9+7;
int calc(int n) {
    int ret=0;
    for (int l=1,r;l<=n;l=r+1) {
        if (n/l!=0) r=min(n/(n/l),n);
        else r=n;
        ret=(ret+(n/l)*(r-l+1)*(l+r)/2%mo)%mo;
    }
    return (n*n%mo-ret%mo+mo)%mo;
}
int FHT(int n,int m){ return calc(n)*calc(m)%mo;}
signed main()
{
    int n,m; scanf("%lld%lld",&n,&m);
    printf("%lld\n",FHT(n,m));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ljc20020730/p/9862068.html