HDU 5780 gcd(数论)

201 篇文章 10 订阅

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值