hdu 5780 gcd(线性筛+快速幂+数论)

题目描述

传送门

题目大意:
1<=a,b<=ngcd(xa1,xb1)

题解

首先对于 gcd(xa1,xb1) 进行化简
更相减损 gcd(a,b)=gcd(ab,b)
那么 gcd(xa1,xb1)=gcd(xaxb,xb1)=gcd(xb(xab1),xb1)
又因为 xb , xb1 互质,所以
gcd(xa1,xb1)=gcd(xab1,xb1)
gcd(xa1,xb1)=xgcd(a,b)1

那么原始的式子就变成了 1<=a,b<=nxgcd(a,b)1

k=1n(xk1)i=1nj=1n[gcd(i,j)=k]

k=1n(xk1)i=1nkj=1nk[gcd(i,j)=1]

因为 [n=1]=d|nμ(d) ,所以
k=1n(xk1)i=1nkj=1nkd|gcd(i,j)μ(d)

k=1n(xk1)d=1nki=1nk[d|i]j=1nk[d|j]μ(d)

k=1n(xk1)d=1nknkd2μ(d)

这样可以 O(Tn34) 利用分块求解,但是如何继续优化呢?
考虑对于 F[m]=mi=1mj=1[gcd(i,j)=1] 进行更直观的理解
F[m]=2(a=1mb=1a[gcd(a,b)=1])1

F[m]=2(a=1mϕ(a))1

对于 (xk1) 这一部分,我们对于每一块讲将所有的-1提出来,然后利用等比数列求和公式计算即可。

代码

#include
#include
#include
#include
#include
#define LL long long 
#define N 1000003
#define p 1000000007
using namespace std;
int n,T,prime[N],pd[N],mu[N],SUM[N];
LL a[N],x,ans,smu[N],phi[N];
LL gcd(LL x,LL y)
{
    LL r;
    while (y) {
        r=x%y;
        x=y; y=r;
    } 
    return x;
}
void init()
{
    mu[1]=1; phi[1]=1;
    for (int i=2;i<=1000000;i++) {
        if (!pd[i]) {
            prime[++prime[0]]=i;
            mu[i]=-1; phi[i]=i-1;
        }
        for (int j=1;j<=prime[0];j++) {
            if (i*prime[j]>1000000) break;
            pd[i*prime[j]]=1;
            if (i%prime[j]==0) {
                mu[i*prime[j]]=0;
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else {
                mu[i*prime[j]]=-mu[i];
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
        }
    }
    for (int i=1;i<=1000000;i++) phi[i]=(phi[i]+phi[i-1])%p;
}
LL quickpow(LL num,int x)
{
    LL ans=1; LL base=num%p;
    while (x) {
        if (x&1) ans=ans*base%p;
        x>>=1;
        base=base*base%p;
    }
    return ans;
}
int main()
{
    scanf("%d",&T);
    init();
    while (T--) {
        scanf("%I64d%d",&x,&n);
        ans=0; int j=0;
        for (int i=1;i<=n;i=j+1) {
            j=min(n/(n/i),n);
            LL t=2*phi[n/i]-1; t%=p;  
            LL a1=quickpow(x,i); LL q=quickpow(x,(j-i+1));
            LL t1=a1*(q-1)%p*quickpow(x-1,p-2)%p-(j-i+1);
            //cout<" "<*t1%p)%p;
        }
        if (x==1) printf("0\n");
        else  printf("%I64d\n",(ans%p+p)%p);
    }
}

你可能感兴趣的:(数论,反演)