BZOJ2154+BZOJ2693 Crash的数字表格&jzptab-莫比乌斯反演

传送门
BZOJ2154
题意:

ni=1mj=1lcm(i,j) ∑ i = 1 n ∑ j = 1 m l c m ( i , j )

Solution:

看到此题首先想到这步:

i=1nj=1nlcm(i,j)=i=1nj=1nijgcd(i,j) ∑ i = 1 n ∑ j = 1 n l c m ( i , j ) = ∑ i = 1 n ∑ j = 1 n i j g c d ( i , j )

然后开始拼命化简,未果

听完dalao的提示:莫比乌斯反演题一定要想办法搞掉分母,想办法枚举约数

想到了下一步:

=min(n,m)d=1dndi=1mdj=1ij[gcd(i,j)==1] = ∑ d = 1 m i n ( n , m ) d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i j [ g c d ( i , j ) == 1 ]

就这样把式子拆成了两部分,现在我们要做的就是化简后面的部分:

=min(n,m)d=1dmin(nd,md)g=1μ(g)ndgi=1mdgj=1igjg = ∑ d = 1 m i n ( n , m ) d ∑ g = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( g ) ∑ i = 1 ⌊ n d g ⌋ ∑ j = 1 ⌊ m d g ⌋ i g ∗ j g

=min(n,m)d=1dmin(nd,md)g=1μ(g)g2ndg(ndg+1)2mdg(mdg+1)2 = ∑ d = 1 m i n ( n , m ) d ∑ g = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( g ) ∗ g 2 ∗ ⌊ n d g ⌋ ( ⌊ n d g ⌋ + 1 ) 2 ∗ ⌊ m d g ⌋ ( ⌊ m d g ⌋ + 1 ) 2

运用传统的分块优化就可以求出答案了

代码:

#include
#include
using namespace std;
const int N=10000010;
const int mod=20101009;
bool vis[N];
int sum[N],n,m;
int prim[1000010],cnt;
int ans;
int mu[N];
void get_prim(int x)
{
    mu[1]=1;sum[1]=1;
    vis[1]=1;
    for (int i=2;i<=x;i++)
    {
        if (!vis[i]) prim[++cnt]=i,mu[i]=-1;
        for (int j=1;j<=cnt&&i*prim[j]<=x;j++)
        {
            vis[i*prim[j]]=1;
            if (i%prim[j]==0) {mu[i*prim[j]]=0;break;}
            mu[i*prim[j]]=-mu[i];
        }
        sum[i]=(1ll*sum[i-1]+1ll*i*i*mu[i])%mod;
    }
}
int SUM(int x,int y)
{
    return (1ll*(1ll*x*(x+1)/2)%mod*((1ll*y*(y+1)/2)%mod))%mod;
}
int calc(int x,int y)
{
    int last=0,t=min(x,y);
    int cnt=0;
    for (int i=1;i<=t;i=last+1)
    {
        last=min(x/(x/i),y/(y/i));
        cnt=(cnt+(sum[last]-sum[i-1])%mod*1ll*SUM(x/i,y/i)%mod+mod)%mod;
    }
    return cnt;
}
int main()
{
    scanf("%d%d",&n,&m);
    get_prim(min(n,m));
    int last=0,t=min(n,m);
    for (int i=1;i<=t;i=last+1)
    {
        last=min(n/(n/i),m/(m/i));
        ans=(1ll*ans+(1ll*(last+i)*(last-i+1)/2)%mod*1ll*calc(n/i,m/i))%mod;
    }
    printf("%d",ans);
} 

加强版:BZOJ2693

加入了多组数据,导致了上面的做法会T

所以我们需要优化

再次变形上面的式子:设 D=dg D = d g

=min(n,m)D=1nD(nD+1)2mD(mD+1)2g|DDgμ(g)g2 = ∑ D = 1 m i n ( n , m ) ⌊ n D ⌋ ( ⌊ n D ⌋ + 1 ) 2 ∗ ⌊ m D ⌋ ( ⌊ m D ⌋ + 1 ) 2 ∑ g | D D g ∗ μ ( g ) ∗ g 2

我们观察后面的那一坨东西:

μ(g)g2 μ ( g ) ∗ g 2 是积性函数,设 f(g)=μ(g)g2 f ( g ) = μ ( g ) ∗ g 2 ,那么 g|DDgμ(g) ∑ g | D D g ∗ μ ( g ) 相当于是 idf i d ∗ f ,两个积性函数的卷积还是积性函数,所以后面的那一坨是个积性函数,这样我们就可以再线性筛的时候顺便求一下就可以了,

这样复杂度就少了 n n

代码:

#include
#include
using namespace std;
const int N=10000010;
const int mod=100000009;
bool vis[N];
int sum[N],n,m,T;
int prim[1000010],cnt;
int ans;
int mu[N];
void get_prim(int x)
{
    mu[1]=1;
    vis[1]=1;
    for (int i=2;i<=x;i++)
    {
        if (!vis[i]) prim[++cnt]=i,mu[i]=(i-1ll*i*i)%mod;
        for (int j=1;j<=cnt&&i*prim[j]<=x;j++)
        {
            vis[i*prim[j]]=1;
            if (i%prim[j]==0) {mu[i*prim[j]]=1ll*prim[j]*mu[i]%mod;break;}
            mu[i*prim[j]]=(1ll*mu[prim[j]]*mu[i])%mod;
        }
    }
    for (int i=1;i<=x;i++)
        sum[i]=(sum[i-1]+mu[i])%mod;

}
int SUM(int x,int y)
{
    return (1ll*(1ll*x*(x+1)/2)%mod*((1ll*y*(y+1)/2)%mod))%mod;
}
int calc(int x,int y)
{
    int last=0,t=min(x,y);
    int cnt=0;
    for (int i=1;i<=t;i=last+1)
    {
        last=min(x/(x/i),y/(y/i));
        cnt=(cnt+(sum[last]-sum[i-1])%mod*1ll*SUM(x/i,y/i)%mod+mod)%mod;
    }
    return cnt;
}
int main()
{
    get_prim(10000000);
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        ans=0;
        int last=0,t=min(n,m);
        ans=calc(n,m);
        printf("%d\n",ans);
    }
} 

你可能感兴趣的:(数论,莫比乌斯反演)