HDU5382 GCD?LCM!

題目鏈接

題目大意

令:

F(n)=i=1nj=1n[lcm(i,j)+gcd(i,j)n],S(n)=i=1nF(i).

T 次詢問S(n) mod 258280327 的值.

T105,n106.

題解

又是這種一看題解就覺得自己也能推的數論題….

首先看數據範圍,目測是先預處理所有答案然後O(1) 詢問的.
那麼預處理時肯定要考慮F(n)F(n1) 的關係.

F(n)=i=1nj=1n[lcm(i,j)+gcd(i,j)n]=i=1nj=1n[lcm(i,j)+gcd(i,j)n1]i=1nj=1n[lcm(i,j)+gcd(i,j)=n1].

G(n)=ni=1nj=1[lcm(i,j)+gcd(i,j)=n] ,先把這個放在一邊.

比較上面得到的這個東西左邊那個二重和式和F(n1) 的表達式,我們可以把其中i=n 或者j=n 的情況特殊討論一下,容易得到此時一定滿足lcm(i,j)+gcd(i,j)n1 ,於是上式就變成了這樣:

F(n)=F(n1)+2n1G(n1).

然後再來推G(n) ,這裏運用的還是通常的枚舉gcd 的思路:

G(n)=i=1nj=1n[lcm(i,j)+gcd(i,j)=n]=d=1ni=1ndj=1nd[ij][ijd+d=n]=dni=1ndj=1nd[ij][ij=nd1]=dnH(nd1).

其中

H(n)=dn[dnd].

喜聞樂見的是H(n) 是個積性函數,可以愉快地線性篩.
具體來說是這樣的:
對素數p ,H(pk)=2(k1) .
考慮篩到prod=iprime[j] 時,prime[j]prod 的最小素因子,H(prod)H(i) 的關係.
i=kprime[j]t(kprime[j]) ,那麼H(k)=H(i)2,prod=kprime[j]t+1 .
d 滿足dkd ,D 滿足DprodD .
那麼D 的約數中要麼不包含prime[j] ,即Dd 一一對應,方案數有H(k)=H(i)2 種;要麼包含prime[j]t+1 ,即D=dprime[j]t+1 ,所以這樣的Dd 也存在一一對應關係,方案數也是H(k)=H(i)2 .
所以H(prod)=H(i) .

然後可以O(nlnn) 計算出G(n) ,再O(n) 計算F(n)S(n) ,就做完了.

代碼

#include<cstdio>
#include<cstring>
const int N=1e6+5,mod=258280327;
int prime[N],H[N],G[N],F[N],S[N];
bool is_prime[N];
void sieve(){
    memset(is_prime,true,N);
    H[1]=1;
    for(int i=2,tot_prime=0;i<N;++i){
        if(is_prime[i]){
            prime[tot_prime++]=i;
            H[i]=2;
        }
        for(int j=0,prod;j<tot_prime&&(prod=i*prime[j])<N;++j){
            is_prime[prod]=false;
            if(i%prime[j])H[prod]=H[i]<<1;
            else{
                H[prod]=H[i];
                break;
            }
        }
    }
}
inline void mod_add(int &a,int b){
    if((a+=b)>=mod)a-=mod;
}
void solve(){
    sieve();
    memset(G,0,N<<2);
    for(int d=1;d<N;++d){
        for(int k=1,n;(n=k*d)<N;++k){
            mod_add(G[n],H[k-1]);
        }
    }
    F[1]=S[1]=1;
    for(int n=2;n<N;++n){
        F[n]=((F[n-1]+(n<<1)-1-G[n-1])%mod+mod)%mod;
        if((S[n]=S[n-1]+F[n])>=mod)S[n]-=mod;
    }
}
inline void rd(int &res){
    res=0;
    char c;
    while(c=getchar(),c<48);
    do res=(res<<3)+(res<<1)+(c^48);
        while(c=getchar(),c>47);
}
int main(){
    int cas,n;
    rd(cas);
    solve();
    while(cas--){
        rd(n);
        printf("%d\n",S[n]);
    }
    return 0;
}
/*

    Jul.05.16

    Tags:number theory,sieve
    Submissions:1

    Memory(KB) 18340
    Time(ms) 234
    Length(Bytes) 990

*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章