洛谷P1829 [国家集训队]Crash的数字表格 / JZPTAB (莫比乌斯反演+数论分块+线性筛)

洛谷P1829 [国家集训队]Crash的数字表格 / JZPTAB (莫比乌斯反演+数论分块+线性筛)

题目链接:https://www.luogu.com.cn/problem/P1829
题目大意:给定一组 n , m n,m n,m ,求 ∑ i = 1 n ∑ j = 1 m l c m ( i , j )   m o d   20101009 \sum\limits_{i=1}^n\sum\limits_{j=1}^mlcm(i,j) \space mod\space 20101009 i=1nj=1mlcm(i,j) mod 20101009 的值。
题解:直接上推导式子的过程。根据数论函数求和的题目的一些套路,我们可以尝试把原式转化为可以应用数论分块的求和式,从而降低求和的时间复杂度。不妨先设 n ≤ m n\le m nm
原式: ∑ i = 1 n ∑ j = 1 m l c m ( i , j ) \sum\limits_{i=1}^n\sum\limits_{j=1}^mlcm(i,j) i=1nj=1mlcm(i,j)
= ∑ i = 1 n ∑ j = 1 m i j g c d ( i , j ) =\sum\limits_{i=1}^n\sum\limits_{j=1}^m\frac{ij}{gcd(i,j)} =i=1nj=1mgcd(i,j)ij
= ∑ i = 1 n ∑ j = 1 m ∑ d = 1 n i j d [ g c d ( i , j ) = d ] =\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{d=1}^n\frac{ij}{d}[gcd(i,j)=d] =i=1nj=1md=1ndij[gcd(i,j)=d]
= ∑ d = 1 n ∑ i = 1 n ∑ j = 1 m i j d [ g c d ( i , j ) = d ] =\sum\limits_{d=1}^n\sum\limits_{i=1}^n\sum\limits_{j=1}^m\frac{ij}{d}[gcd(i,j)=d] =d=1ni=1nj=1mdij[gcd(i,j)=d]
= ∑ d = 1 n d 2 ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i j d [ g c d ( i , j ) = 1 ] =\sum\limits_{d=1}^nd^2\sum\limits_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{d}\rfloor}\frac{ij}{d}[gcd(i,j)=1] =d=1nd2i=1dnj=1dmdij[gcd(i,j)=1]
= ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i j ε ( g c d ( i , j ) ) =\sum\limits_{d=1}^nd\sum\limits_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{d}\rfloor}ij\varepsilon(gcd(i,j)) =d=1ndi=1dnj=1dmijε(gcd(i,j))
= ∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i j ∑ t ∣ i , t ∣ j μ ( t ) =\sum\limits_{d=1}^nd\sum\limits_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{d}\rfloor}ij\sum\limits_{t\mid i,t\mid j}\mu(t) =d=1ndi=1dnj=1dmijti,tjμ(t)
= ∑ d = 1 n d ∑ t = 1 ⌊ n d ⌋ μ ( t ) t 2 ∑ i = 1 ⌊ n d t ⌋ ∑ j = 1 ⌊ m d t ⌋ i j =\sum\limits_{d=1}^nd\sum\limits_{t=1}^{\lfloor \frac{n}{d}\rfloor}\mu(t)t^2\sum\limits_{i=1}^{\lfloor \frac{n}{dt}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{dt}\rfloor}ij =d=1ndt=1dnμ(t)t2i=1dtnj=1dtmij
f ( x , y ) = ∑ t = 1 x μ ( t ) t 2 ∑ i = 1 ⌊ x t ⌋ ∑ j = 1 ⌊ y t ⌋ i j   ( x ≤ y ) f(x,y)=\sum\limits_{t=1}^{x}\mu(t)t^2\sum\limits_{i=1}^{\lfloor \frac{x}{t}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{y}{t}\rfloor}ij\space(x\le y) f(x,y)=t=1xμ(t)t2i=1txj=1tyij (xy)
显然,对于式子的后半部分 ∑ i = 1 ⌊ x t ⌋ ∑ j = 1 ⌊ y t ⌋ i j \sum\limits_{i=1}^{\lfloor \frac{x}{t}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{y}{t}\rfloor}ij i=1txj=1tyij ,我们可以应用数论分块进行处理,而对于前半部分的 μ ( t ) t 2 \mu(t)t^2 μ(t)t2 ,我们可以预处理其的前缀和存放在数组 s s s,这样 ∑ t ⌊ n ⌊ n t ⌋ ⌋ μ ( t ) t 2 = s [ ⌊ n ⌊ n t ⌋ ⌋ ] − s [ t − 1 ] \sum\limits_t^{\lfloor\frac{n}{\lfloor\frac{n}{t}\rfloor}\rfloor}\mu(t)t^2=s[\lfloor\frac{n}{\lfloor\frac{n}{t}\rfloor}\rfloor]-s[t-1] ttnnμ(t)t2=s[tnn]s[t1]
设原式为 g ( x , y )   ( x ≤ y ) g(x,y)\space(x\leq y) g(x,y) (xy) ,那么原式: g ( x , y ) = ∑ d = 1 x d f ( ⌊ x d ⌋ , ⌊ y d ⌋ ) g(x,y)=\sum\limits_{d=1}^xdf(\lfloor\frac{x}{d}\rfloor,\lfloor\frac{y}{d}\rfloor) g(x,y)=d=1xdf(dx,dy) 。此时可以 O ( x + y ) O(x+y) O(x+y) 求出函数 g ( x , y ) g(x,y) g(x,y) 的值。
下面是AC代码。

#include
#include
#include
using namespace std;
int N;
const int M=20101009;
vector<int> prime,part,mu,s;
void init(){
    s=part=vector<int>(N,0);
    mu=vector<int>(N,-1);
    s[1]=mu[1]=1;
    for(int i=2;i<N;++i){
        s[i]=((mu[i]*i*i)%M+M+s[i-1])%M;
        if(!part[i]){
            prime.push_back(i);
            part[i]=i;
        }
        for(auto j:prime){
            if(i*j>=N) break;
            part[i*j]=j;
            if(i%j==0){
                mu[i*j]=0;
                break;
            }else mu[i*j]=-mu[i];
        }
    }
}
int f(int x,int y){
    if(x>y) swap(x,y);
    int res=0;
    for(int l=1,r;l<=x;l=r+1){
        r=min(x/(x/l),y/(y/l));
        int t1=(((x/l)*(x/l+1))>>1)%M,t2=(((y/l)*(y/l+1))>>1)%M,t3=((s[r]-s[l-1])%M+M)%M;
        res=(res+t1*(t2*t3%M)%M)%M;
    }
    return res;
}
int g(int x,int y){
    if(x>y) swap(x,y);
    int res=0;
    for(int l=1,r;l<=x;l=r+1){
        r=min(x/(x/l),y/(y/l));
        res=(res+((((((r-l+1)*(l+r)>>1)%M))%M)*f(x/l,y/l))%M)%M;
    }
    return res;
}
signed main(){
    int n,m;
    cin>>n>>m;
    N=max(n,m)+5;
    init();
    cout<<g(n,m)<<endl;
}

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