bzoj 2693 jzptab(莫比乌斯反演+积性函数线性筛一般套路)

题目:
Description
∑ i = 1 n ∑ j = 1 m l c m ( i , j ) \sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j) i=1nj=1mlcm(i,j)
Input
一个正整数T表示数据组数,接下来T行每行两个正整数表示N和M
(T<=10000,N,M<=10000000)
Output
T行,每行一个整数表示第i组数据的结果
Sample Input
1
4 5
Sample Output
122

参考博客:积性函数
参考1
参考2

这题先参考上一篇博文得到的最终式子:last

我们会得到这个式子:
∑ i = 1 n ∑ j = 1 m l c m ( i , j ) = \sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)= i=1nj=1mlcm(i,j)= ∑ d = 1 n d ∑ i = 1 n / d u ( i ) ∗ i 2 s u m ( n d ∗ i , m d ∗ i ) \sum_{d=1}^{n}d\sum_{i=1}^{n/d}u(i)*i^2sum(\frac{n}{d*i},\frac{m}{d*i}) d=1ndi=1n/du(i)i2sum(din,dim)

然后我们可以化简为: 令 T = d ∗ i = ∑ T = 1 n s u m ( n T , m T ) ∑ i ∣ T i 2 u ( i ) T i \frac{令T=d*i}{=}\sum_{T=1}^{n}sum(\frac{n}{T},\frac{m}{T})\sum_{i|T}i^2u(i)\frac{T}{i} =T=diT=1nsum(Tn,Tm)iTi2u(i)iT
这个化简说实话,我是想不到。


我们设 h ( T ) h(T) h(T) = ∑ i ∣ T i 2 u ( i ) T i =\sum_{i|T}i^2u(i)\frac{T}{i} =iTi2u(i)iT,可以知道函数h为积性函数,因为我们知道正因子之和是积性函数,莫比乌斯函数也是积性函数,两个积性函数相乘也为积性函数,如何判断此函数是否为积性函数,像我这种萌新,更多的只能依靠平时的积累加经验(大部分题目给的,你都能看成积性函数去做,只要你能想到推导公式)。

既然现在我们知道函数h为积性函数,那么我们就可以线性筛把函数h求出来,再弄个前缀和就行了。

线性筛分为3部分:
1.n本身是素数,这个根据积性函数的定义可得,很容易求。
2.i%prime[j]!=0,这个也是根据积性函数的性质可得,即f(a)f(b)=f(ab)。
3.i%prime[j]==0,这个可能需要找规律加灵光一闪。

1,对于素数p,它的函数 h ( p ) = p − p ∗ p h(p)=p-p*p h(p)=ppp;
2,i%prime[j]!=0,此时 h [ i ∗ p r i m e [ j ] ] = h [ i ] ∗ h[i*prime[j]]=h[i]* h[iprime[j]]=h[i] h [ p r i m e [ j ] ] h[prime[j]] h[prime[j]]


3,i%prime[j]==0,此时 h [ i ∗ p r i m e [ j ] ] = h [ i ] ∗ h[i*prime[j]]=h[i]* h[iprime[j]]=h[i] p r i m e [ j ] prime[j] prime[j],i*prime[j] 的唯一分解可以写成一部分素数乘积 i 与 另一部分在前一部分中出现过的素数的乘积 j,也就是存在质因子的指数大于1,它新增的每一个因子的 μ 值都是0,没有意义,只有统计时D变成了原来的 j 倍

所以 此时 h ( i ∗ p r i m e [ j ] ) = h ( i ) ∗ p r i m e [ j ] h( i*prime[j]) = h( i ) * prime[j] h(iprime[j])=h(i)prime[j]
最后我们处理下函数h的前缀和,再加个分块就可以了。



#include<bits/stdc++.h>

using namespace std;
typedef long long LL;

const LL mod=1e8+9;
const int N=1e7+10;

int prime[N],tot;
bool vis[N];

LL f[N],sum[N];
int n,m;

void init()
{
    f[1]=sum[1]=1LL;
    for(int i=2;i<N;i++)
    {
        if(!vis[i]){
            prime[++tot]=i;
            f[i]=(i-1LL*i*i+mod)%mod;///i为素数
        }
        for(int j=1;j<=tot&&i*prime[j]<N;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0){///第三种情况
                f[i*prime[j]]=f[i]*prime[j]%mod;
                break;
            }
            ///第二种情况
            f[i*prime[j]]=f[i]*f[prime[j]]%mod;
        }
        sum[i]=(sum[i-1]+f[i])%mod;

    }
}

LL Sum(LL x,LL y){

    x=((x+1)*x/2)%mod;
    y=((y+1)*y/2)%mod;
    return x*y%mod;
}

LL solve()
{
    int r;
    LL re=0;
    ///整数分块
    for(int l=1;l<=n;l=r+1)
    {
        r=min(n/(n/l),m/(m/l));
        re=(re+Sum(n/l,m/l)*(sum[r]-sum[l-1])%mod)%mod;
    }
    return re;
}

int main()
{
    init();

    int ncase;
    scanf("%d",&ncase);

    while(ncase--)
    {
        scanf("%d%d",&n,&m);
        if(n>m) swap(n,m);

        printf("%lld\n",solve());
    }
    return 0;
}




我的标签:做个有情怀的程序员。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值