题目描述:戳这里
题解:
首先有一个结论:
gcd(xa−1,xb−1)=xgcd(a,b)−1
g
c
d
(
x
a
−
1
,
x
b
−
1
)
=
x
g
c
d
(
a
,
b
)
−
1
推导过程可以参考一下辗转相减法。
(xa−1,xb−1)=(xa−b,xb−1)
(
x
a
−
1
,
x
b
−
1
)
=
(
x
a
−
b
,
x
b
−
1
)
我们重复这一个步骤,就会发现其实幂次在辗转相减,而外面总会有一个-1,所以可以最后一定会变为
xgcd(a,b)−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‾√)
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;
}