HDU 5780 gcd

版权声明:原创文章,转载要注明作者哦 https://blog.csdn.net/DYT_B/article/details/82226249

题目描述:戳这里

题解:
首先有一个结论: g c d ( x a 1 , x b 1 ) = x g c d ( a , b ) 1
推导过程可以参考一下辗转相减法。
( x a 1 , x b 1 ) = ( x a b , x b 1 )
我们重复这一个步骤,就会发现其实幂次在辗转相减,而外面总会有一个-1,所以可以最后一定会变为 x g c d ( a , b ) 1
那么知道了这点以后,就转化为比较熟悉的求1~n的gcd的模型了。
那么我们令gcd(a,b)=k,枚举k,求的就是n/k范围内在互质的数的对数f[n/k],因为a,b可以互换,所以还要乘以2然后-1。
那么可以预处理一下f。
但是这样要枚举k,时间复杂度为O(n)。
但是我们发现n/k可以除法分块,时间复杂度就压到 O ( n ) 了。
代码如下:

#include<cstdio>
#include<string>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1000005,tt=1e9+7;
int T,m,su[maxn];
ll X,n,f[maxn],inv[maxn],ans;
bool vis[maxn];
void make_f(){
    f[1]=1;
    for (int i=2;i<=1000000;i++){
        if (!vis[i]) f[i]=i-1ll,su[++m]=i;
        for (int j=1;j<=m&&i*su[j]<=1000000;j++){
            vis[i*su[j]]=1;
            if (i%su[j]==0) {f[i*su[j]]=f[i]*su[j]; break;}
            f[i*su[j]]=f[i]*(su[j]-1);
        }
    }
    for (int i=2;i<=1000000;i++) f[i]=(f[i-1]+f[i]*2%tt)%tt;
    inv[1]=1ll;
    for (int i=2;i<=1000000;i++) inv[i]=1ll*(tt-tt/i)*inv[tt%i]%tt;
}
ll qsm(ll x,ll y){
    ll ret=1ll;
    while (y){
        if (y%2==1) ret=ret*x%tt;
        x=x*x%tt; y/=2;
    }
    return ret;
}
void doit(){
    ll r=0; ans=0;
    for (int i=1;i<=n;i=r+1){
        r=n/(n/i);
        ans=(ans+f[n/i]*qsm(X,i)%tt*(qsm(X,r-i+1)+tt-1)%tt*inv[X-1]%tt)%tt;
    }
    printf("%lld\n",(ans+tt-1ll*n*n%tt)%tt);
}
int main(){
    scanf("%d",&T);
    make_f();
    while (T--){
        scanf("%lld %lld",&X,&n);
        if (X==1) {printf("0\n"); continue;}
        doit();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/DYT_B/article/details/82226249