[BZOJ2154]Crash的数字表格(数论)

=== ===

这里放传送门

=== ===

题解

题目要求

i=1nj=1mlcm(i,j)

就直接画柿子就好啦。。
1.由于最小公倍数等于两数乘积除以最大公因数,柿子变为:
i=1nj=1mijgcd(i,j)

2.枚举两数最大公因数d,显然两个数分别除以它们的最大公因数得到的数应该是互质的:
i=1nj=1md=1n[d|i][d|j][gcd(id,jd)=1]ijd

3.经典的画柿子方法:令 i=id j=jd ,代换过去就有:
d=1ndi=1ndj=1md[gcd(i,j)=1]ij

4.后半段柿子中出现了互质数对之积的和。为了让柿子看起来好看一点就把它拿出来单独算吧。
记为 s(n,m) ,即 i=1nj=1m[gcd(i,j)=1]ij 。接下来对 s(n,m) 化简。首先枚举约数变成:
d=1ni=1nj=1m[d|i][d|j]μ(d)ij
再设 i=id,j=jd 进行替换变成
d=1nμ(d)d2i=1ndj=1mdij

5.后半段又出现了一个范围内所有数对之积的和,表示为 sum(n,m) 。显然 sum(n,m) 是一个可以在 O(1) 的时间内算出单个值的函数,公式为 sum(n,m)=n(n+1)m(m+1)4 。那么上面的 s(n,m) 就可以表示成 d=1nμ(d)d2sum(nd,md) ,利用 nnd 的分块做法,我们可以发现 s(n,m) 是一个可以在 O(n+m) 的时间内算出单个值的函数。
6.求出 s(n,m) 以后第三步中画出的柿子就可以表示成 d=1ns(nd,md) ,也可以用 nnd 的分块做法做。两层 O(n+m) 套起来时间复杂度基本上是 O(n+m) 的。那么线性筛搞到 μ 函数啥的用来算就可以啦

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Mod=20101009;
int n,m,mu[10000010],prm[10000010],sqr[10000010];
bool ext[10000010];
long long sum,ans,h,t1,t2,l,w1,w2,hd,tl;
long long Sum(int n,int m){
    long long a1,a2;
    a1=(long long)n*(n+1)/2%Mod;
    a2=(long long)m*(m+1)/2%Mod;
    return a2*a1%Mod; //注意取模
}
long long s(int n,int m){
    sum=0;
    hd=1;
    while (hd<=n){
        w1=n/(n/hd);w2=m/(m/hd);
        tl=min(w1,w2);
        sum+=(long long)(sqr[tl]-sqr[hd-1])%Mod*Sum(n/hd,m/hd)%Mod; //注意强转
        hd=tl+1;sum%=Mod; //注意取模
    }
    return sum%Mod;
}
int main()
{
    scanf("%d%d",&n,&m);
    if (n>m) swap(n,m);
    mu[1]=1;sqr[1]=1;
    for (int i=2;i<=n;i++){
        if (ext[i]==false){
            prm[++prm[0]]=i;
            mu[i]=-1;
        }
        for (int j=1;j<=prm[0];j++){
            if (i*prm[j]>n) break;
            ext[i*prm[j]]=true;
            if (i%prm[j]==0){
                mu[i*prm[j]]=0;break;
            }else mu[i*prm[j]]=-mu[i];
        }
        sqr[i]=(long long)i*i*mu[i]%Mod;
        sqr[i]=(sqr[i]+sqr[i-1])%Mod; //注意取模
    }
    ans=0;h=1;
    while (h<=n){
        t1=n/(n/h);t2=m/(m/h);
        l=min(t1,t2);
        ans+=(long long)(h+l)*(l-h+1)/2%Mod*s(n/h,m/h)%Mod;//注意强转
        h=l+1;ans%=Mod;//注意取模
    }
    printf("%lld\n",(ans%Mod+Mod)%Mod);
    return 0;
}

偏偏在最后出现的补充说明

这题如果全都开成long long会炸内存。。所以要各种强转还要各种取模。。在计算sum函数的时候特别容易炸掉int需要注意。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值