【BZOJ3309】DZY Loves Math(莫比乌斯反演)

题面


i=1aj=1bf(gcd(a,b))

其中, f(x) 表示 x 分解质因数之后,最高的幂次

题解

完全不会莫比乌斯反演了。
先来推式子

d=1ai=1a/dj=1b/d[gcd(i,j)=1]f(d)

d=1af(d)i=1a/dj=1b/d[gcd(i,j)=1]

F(x)=ni=1mj=1[gcd(i,j)=x]
G(x)=x|df(d)=ni=1mj=1[x|gcd(i,j)]
所以 G(x)=[nx][mx]
所以原式可以变为
又有 F(x)=x|dμ(dx)G(d)
所以 F(1)=nd=1μ(d)G(d)
所以所求变为
d=1af(d)i=1a/dμ(i)G(i)

d=1af(d)i=1a/dμ(i)[aid][bid]

老套路了,令 T=id

T=1a[aT][bT]d|Tf(d)μ(Td)

后面这个玩意怎么算呢??
迷茫啊。。。。

考虑枚举的每一个 T=p1a1p2a2...pnan
因为 μ 要非零才有贡献,所以 Td 的每一个质因数最多取 1 ,因此一共有 2n 个对应的 d
假设确定了选择某个质因数 pxax1 ,并且它是最高幂了
那么所有比它低的幂次都可以随意选或者不选,
一共是 2?1 个,不难证明此时 f(d) 的值一样, μ 的值恰好一一对应为 1,1 ,此时的和一定为 0
这样的前提是存在比他低的次幂,也就意味着所有的幂次不全相等。

假设全部相等的时候?
也就是 T=(p1p2p3..pn)a
d=(p1p2..pn)a1 时, f(d)=a1
其他情况下 f(d)=a
先假设所有情况下 f(d)=a
显然最终的和也是 0
但是有一种情况下为 a1 ,因此要对于上面那种情况额外把 1 减掉
因此贡献是 1μ(p1p2p3..pn)=(1)(n+1)

这样就可以算出后面那一坨东西的值了。
至于怎么线性筛?
额外记录每个数出去最小质因子后的数 lst[i]
以及最小质因子的幂次
这样就可以通过 lst[i] 计算出结果了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 10000000
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
bool zs[MAX+1];
int pri[MAX+1],tot,g[MAX+1],lst[MAX+1],fp[MAX+1];
void pre()
{
    for(int i=2;i<=MAX;++i)
    {
        if(!zs[i])pri[++tot]=i,lst[i]=g[i]=fp[i]=1;
        for(int j=1;j<=tot&&i*pri[j]<=MAX;++j)
        {
            int x=i*pri[j];
            zs[x]=true;
            if(i%pri[j]==0)
            {
                lst[x]=lst[i];
                fp[x]=fp[i]+1;
                if(lst[x]==1)g[x]=1;
                else g[x]=(fp[lst[x]]==fp[x]?-g[lst[x]]:0);
                break;
            }
            lst[x]=i;fp[x]=1;g[x]=(fp[i]==1?-g[i]:0);
        }
    }
    for(int i=1;i<=MAX;++i)g[i]+=g[i-1];
}
int main()
{
    pre();
    int T=read();
    while(T--)
    {
        int a=read(),b=read();
        if(a>b)swap(a,b);
        ll ans=0;
        for(int i=1,j;i<=a;i=j+1)
        {
            j=min(a/(a/i),b/(b/i));
            ans+=1ll*(a/i)*(b/i)*(g[j]-g[i-1]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30974369/article/details/79910567
今日推荐