Description
小白学会了求最大公约数,于是打算解决一个问题:给定x,n,求∑gcd(x^a-1,x^b-1) (1≤a,b≤n)
Input
第一行输入一个整数T(1≤T≤300)
每组数据有一行,有两个整数x和n(1≤x,n≤1000000)
Output
对于每组数据,输出一行,结果显然很大,对1e9+7取模
Sample Input
5
3 1
4 2
8 7
10 5
10 8
Sample Output
2
24
2398375
111465
111134466
Solution
不妨假设b>a,那么由知
故问题变成了求
先看怎么求num[d],即1<=a,b<=n中有多少对(a,b)满足gcd(a,b)=d
考虑a < b的情况,设a=i*d,b=j*d,则有1<=i < j<=n/d且gcd(i,j)=1,固定j,i有phi(j)种取值,a=b时只有a=b=d一种取值,故有
由于此题有300组用例,所以O(n)的算法也不行,但注意到num[d]只和n/d有关,只要n/d相同,num[d]就相同,根据n/d的值将1~n分段,n/d值相同的一段中x^a-1可以通过等比数列求和得到,除法注意用逆元和x-1=0的情况,时间复杂度O(n+T√nlogn)
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define mod 1000000007ll
#define maxn 1111111
typedef long long ll;
int euler[maxn],prime[maxn],res;
ll sum[maxn];
void get_euler(int n)
{
memset(euler,0,sizeof(euler));
euler[1]=sum[1]=1;
res=0;
for(int i=2;i<=n;i++)
{
if(!euler[i])euler[i]=i-1,prime[res++]=i;
for(int j=0;j<res&&prime[j]*i<=n;j++)
{
if(i%prime[j]) euler[prime[j]*i]=euler[i]*(prime[j]-1);
else
{
euler[prime[j]*i]=euler[i]*prime[j];
break;
}
}
sum[i]=(sum[i-1]+euler[i])%mod;
}
}
ll mod_pow(ll a,ll b)
{
ll ans=1ll;
while(b)
{
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
ll get(int x,int a,int b)
{
ll ans=(mod_pow(x,b+1)-mod_pow(x,a+1)+mod)%mod;
ans=ans*mod_pow(x-1,mod-2)%mod;
ans=(ans+a-b+mod)%mod;
return ans;
}
int T,x,n;
int main()
{
get_euler(maxn-10);
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&x,&n);
if(x==1)
{
printf("0\n");
continue;
}
ll ans=0;
for(int i=1,pre;i<=n;i=pre+1)
{
pre=n/(n/i);
ans=(ans+(2ll*sum[n/i]-1)*get(x,i-1,pre)%mod)%mod;
}
printf("%I64d\n",ans);
}
return 0;
}