1. 程式人生 > >HDU5780 gcd 尤拉函式

HDU5780 gcd 尤拉函式

gcd

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 732    Accepted Submission(s): 254

Problem Description

Little White learned the greatest common divisor, so she plan to solve a problem: given x, n, query ∑gcd(x^a−1-1,x^b−1) (1≤a,b≤n)

Input

The first line of input is an integer T ( 1≤T≤300) For each test case ,the single line contains two integers x and n ( 1≤x,n≤1000000)

Output

For each testcase, output a line, the answer mod 1000000007

Sample Input  

5 

3 1 

4 2 

8 7 

10 5 

10 8

Sample Output  

2 

24 

2398375 

111465 

111134466

Source

題意:

a的取值範圍是[1,n],b的取值範圍是[1,n],求  gcd((x^a)-1,(x^b)-1)  之和

分析:

首先,gcd(x^a-1,x^b-1)=x^{gcd(a,b)}-1  ,問題轉化為求1到n,最大公約數為d的個數 * (x^d)-1的和

ans=∑s[d]∗(x^​d​​−1),記s[d]=最大公約數為d的個數,也就是1到n/d以內,互質的對數,顯然是尤拉函式的字首和

s[d]=2*(phi[1]+phi[2]+...+phi[n/d])-1

注意到:d不同,但是n/d一樣,也就是s[d]可能有多個相同,比如 10/6 10/7 10/8 10/9 10/10,所以求s[d]相同的項,我們可以用等比公式求和(快速冪+逆元 ),直接進行除法會發生精度誤差,所以考慮求逆元    sum=\frac{a1*(1-q^{n})}{1-q}

所以我們只要找到每一段s[d]就可以 即 r=n/(n/d),r為最後一個相同s[d]的下標,d為第一個s[d]相同的下標

sum=x^{d1}+x^{d2}+x^{d3}+x^{d4}+x^{d5}+x^{d6}..........+x^{r}

sum=\frac{x^{d}*(1-x^{n})}{1-x}

由於第一個下標為d,最後一個下標為r,n=(r-d+1)

sum=\frac{x^{d}*(x^{r-d+1}-1)}{x-1}

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
#define mod 1000000007
ll phi[maxn];
void get_phi()
{
    for(int i=1;i<maxn;i++)
        phi[i]=i;
    for(int i=2;i<maxn;i++)
        if(phi[i]==i)
        for(int j=i;j<maxn;j+=i)
        phi[j]=phi[j]/i*(i-1);

    for(int i=3;i<maxn;i++)
        phi[i]=(phi[i]+phi[i-1])%mod;

    for(int i=2;i<maxn;i++)
        phi[i]=(2*phi[i]+1)%mod;
}
ll quickPow(ll a,ll b)
{
    ll res=1;
    while(b){
        if(b&1) res=(res*a)%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int main()
{
    get_phi();
    int T;
    scanf("%d",&T);
    while(T--){
        ll x,n;
        scanf("%lld%lld",&x,&n);
        if(x==1)
        {
            printf("0\n");
            continue;
        }
        ll ans=0;
        ll inv=quickPow(x-1,mod-2);//費馬小定理求逆元
        for(int i=1;i<=n;i=n/(n/i)+1)
        {
            ll r=n/(n/i);//閉區間終點,i為閉區間起點
            ll part=((quickPow(x,i)*(quickPow(x,r-i+1)-1)%mod)*inv%mod-(r-i+1)+mod)%mod;//等比數列求和
            ans=(ans+(phi[n/i]*part)%mod+mod)%mod;
        }
        printf("%lld\n",(ans+mod)%mod);
    }
    return 0;
}