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

题目描述

传送门

题解

是时候又写一大坨推导公式了。
首先设

s(x,y)=i=1xj=1yij[gcd(i,j)=1]

并且 x<y
利用莫比乌斯反演变形
s(x,y)=i=1xj=1yijd=1x[d|i][d|j]μ(d)

令i=id j=jd
s(x,y)=d=1xμ(d)d2i=1xdj=1ydij

再设

sum(x,y)=i=1xj=1yij

利用等差数列求和公式
sum(x,y)=x(x+1)2y(y+1)2

将sum函数代入s函数得
s(x,y)=d=1xμ(d)d2sum(xd,yd)

好我们现在回到题目

i=1nj=1mlcm[i,j]

n<m
那么
i=1nj=1mijgcd(i,j)

i=1nj=1mijd=1n[d|i][d|j][gcd(i,j)=d]1d

令i=id j=jd
d=1ni=1ndidj=1mdjd[gcd(i,j)=1]1d

d=1ndi=1ndj=1mdij[gcd(i,j)=1]

将s函数代入这个式子得到

d=1nds(nd,md)

sum函数可以在 O(1) 的时间得到
s函数最多有 n+m 种取值,可以在 O(n+m) 的时间得到
那么答案也只有 n+m 种取值,同样可以在 O(n+m) 的时间得到,所以时间复杂度是接近 O(n)

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
const int max_n=1e7+5;
const LL Mod=20101009;
LL n,m,x,y,xx,yy,k,jj,sum,ans,s[max_n];
int mu[max_n],prime[max_n],p[max_n];
inline void get_mu(){
    mu[1]=1;
    for (int i=2;i<=n;++i){
        if (!p[i]){
            prime[++prime[0]]=i;
            mu[i]=-1;
        }
        for (int j=1;j<=prime[0]&&i*prime[j]<=n;++j){
            p[i*prime[j]]=1;
            if (i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            else mu[i*prime[j]]=-mu[i];
        }
    }
    for (LL i=1;i<=n;i++) s[i]=(LL)(s[i-1]+(LL)(i*i*mu[i])%Mod)%Mod;  
}
inline LL Sum(LL x,LL y){
    LL w1=((x+1)*x/2)%Mod;
    LL w2=((y+1)*y/2)%Mod;
    return w1*w2%Mod;
}
int main(){
    scanf("%lld%lld",&n,&m); if (n>m) swap(n,m);
    get_mu(); jj=0;
    for (LL i=1;i<=n;i=jj+1){
        jj=min(n/(n/i),m/(m/i));
        x=n/i,y=m/i; if (x>y) swap(x,y); k=0; sum=0; for (LL j=1;j<=x;j=k+1){ k=min(x/(x/j),y/(y/j)); xx=x/j; yy=y/j;
            sum=(sum+((s[k]-s[j-1])%Mod*Sum(xx,yy)%Mod)%Mod)%Mod;
        }
        ans=(ans+(((i+jj)*(jj-i+1)/2)%Mod*sum%Mod)%Mod)%Mod;
    }
    printf("%lld\n",(ans+Mod)%Mod);
}

你可能感兴趣的:([BZOJ2154]Crash的数字表格(数论))