BZOJ 3309 莫比乌斯反演

题目链接


题意:多组数据,已知 a , b ( &lt; = 1 e 7 ) a,b(&lt;= 1e7) a,b(<=1e7),定义 f ( n ) f(n) f(n) n n n所含质因子的最大幂指数。
∑ i = 1 a ∑ j = 1 b f ( g c d ( i , j ) ) \sum_{i=1}^{a}\sum_{j=1}^bf(gcd(i,j)) i=1aj=1bf(gcd(i,j))

此前写过一个 f ( n ) f(n) f(n) n n n的约数和的题解:我是链接= =

感觉此类题的推导都是一个套路,定义最终答案为 A n s Ans Ans,首先转换枚举变量为 g c d gcd gcd,并设 g ( i ) : g c d = = i g(i):gcd == i g(i):gcd==i的方案数,则:
A n s = ∑ i = 1 m i n ( a , b ) f ( i ) g ( i ) Ans = \sum_{i=1}^{min(a,b)}f(i)g(i) Ans=i=1min(a,b)f(i)g(i)

然后利用莫比乌斯反演,设:
G ( i ) : g c d 为 i 的 倍 数 的 方 案 数 G(i):gcd为i的倍数的方案数 G(i):gcdi,则:
G ( i ) = ⌊ a i ⌋ ⌊ b i ⌋ G(i) = \lfloor\frac{a}{i}\rfloor\lfloor\frac{b}{i}\rfloor G(i)=iaib
G ( i ) = ∑ i ∣ d g ( d )   ⇒   g ( i ) = ∑ i ∣ d μ ( d i ) G ( d ) G(i) = \sum_{i|d}g(d) \space ⇒ \space g(i) = \sum_{i|d}\mu(\frac{d}{i})G(d) G(i)=idg(d)  g(i)=idμ(id)G(d)

g ( i ) g(i) g(i)代入 A n s Ans Ans表达式,得出:
A n s = ∑ i = 1 m i n ( a , b ) f ( i ) ∑ i ∣ d μ ( d i ) ⌊ a d ⌋ ⌊ b d ⌋ Ans = \sum_{i=1}^{min(a,b)}f(i)\sum_{i|d}\mu(\frac{d}{i}) \lfloor\frac{a}{d}\rfloor\lfloor\frac{b}{d}\rfloor Ans=i=1min(a,b)f(i)idμ(id)dadb

转化枚举变量为 d d d,得出:
A n s = ∑ d = 1 m i n ( a , b ) ⌊ a d ⌋ ⌊ b d ⌋ ∑ i ∣ d f ( i ) μ ( d i ) Ans = \sum_{d=1}^{min(a,b)}\lfloor\frac{a}{d}\rfloor\lfloor\frac{b}{d}\rfloor \sum_{i|d}f(i)\mu(\frac{d}{i}) Ans=d=1min(a,b)dadbidf(i)μ(id)


g ( x ) g(x) g(x)(此处的 g ( x ) g(x) g(x)与上面的 g ( x ) g(x) g(x)的定义完全不同):
g ( x ) = ∑ i ∣ x f ( i ) μ ( x i ) g(x) = \sum_{i|x}f(i)\mu(\frac{x}{i}) g(x)=ixf(i)μ(ix)
现在的难点便在于如何利用线性筛快速得出 g ( x ) g(x) g(x)然后再求前缀和了,如果能实现的话我们就可以在 O ( n ) O(n) O(n)复杂度下预处理, O ( T n ) O(T\sqrt n) O(Tn )的复杂度下快速求解了。

此处参考了PoPoQQQ的博客
讲得挺好的,但确实很难理解,自己在纸上模拟了几个例子才想通。
数论真是玄学啊qwq


代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;

const int A = 1e7 + 10;
bool vis[A];
int pri[A],cnt[A],tot;
ll g[A],val[A]; //假设p为i的最小质因子,则cnt[i]:p的次数 val[i]:p^cnt[i]的值

void init(){
    tot = 0;
    for(int i=2 ;i<A ;i++){
        if(!vis[i]){pri[++tot] = i;cnt[i]=1;val[i] = i;g[i] = 1;}
        for(int j=1 ;j<=tot && i*pri[j]<A ;j++){
            vis[i*pri[j]] = 1;
            if(i%pri[j] == 0){
                cnt[i*pri[j]] = cnt[i] + 1;
                val[i*pri[j]] = val[i]*pri[j];
                int now = i/val[i];
                if(now == 1) g[i*pri[j]] = 1;
                else         g[i*pri[j]] = (cnt[now] == cnt[i*pri[j]]?-g[now]:0);
                break;
            }
            cnt[i*pri[j]] = 1;
            val[i*pri[j]] = pri[j];
            g[i*pri[j]] = (cnt[i] == cnt[i*pri[j]]?-g[i]:0);
        }
    }
    for(int i=1 ;i<A ;i++) g[i] += g[i-1];
}

int main(){
    init();
    int T;
    scanf("%d",&T);
    while(T--){
        int a,b,last;
        scanf("%d%d",&a,&b);
        if(a>b) swap(a,b);
        ll ans = 0;
        for(int i=1 ;i<=a ;i=last+1){
            last = min(a/(a/i),b/(b/i));
            ans += 1LL*(a/i)*(b/i)*(g[last]-g[i-1]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值