[HDU 5780] gcd (公式证明)

本文通过一个具体的数学题目,详细解析了一个重要的数学公式:gcd(xa−1,xb−1)=xgcd(a,b)−1,并给出了相应的算法实现。文章还探讨了如何通过枚举和分块等技巧优化算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

做出这题你需要推出一个重要的式子:gcd(xa−1,xb−1)=xgcd(a,b)−1gcd(x^a-1,x^b-1)=x^{gcd(a,b)}-1gcd(xa1,xb1)=xgcd(a,b)1
我这证明可能不算严谨吧。。。。
反正OI不需要证明,只需要感性理解。然而我个人觉得感性理解反而比证明重要啊,证明不就是几个式子套来套去,过几天就忘光了
不妨设a>ba>ba>b,利用根相减损术,可以得到 gcd(xa−1,xb−1)=gcd(xa−xb,xb−1)gcd(x^a-1,x^b-1)=gcd(x^a-x^b,x^b-1)gcd(xa1,xb1)=gcd(xaxb,xb1)
化简得 gcd(xa−1,xb−1)=gcd(xb(xa−b−1),xb−1)gcd(x^a-1,x^b-1)=gcd(x^b(x^{a-b}-1),x^b-1)gcd(xa1,xb1)=gcd(xb(xab1),xb1)
因为 gcd(xb,xb−1)=1gcd(x^b,x^b-1)=1gcd(xb,xb1)=1
所以gcd(xa−1,xb−1)=gcd(xa−b−1,xb−1)gcd(x^a-1,x^b-1)=gcd(x^{a-b}-1,x^b-1)gcd(xa1,xb1)=gcd(xab1,xb1)
至此,我们可以发现 aaabbb 也恰好进行了一次根相减损术的过程。
根相减损术结束条件是其中一边为 000,另一边就是 gcdgcdgcd
不妨设最后a=0a=0a=0,则 bbb 就为原来 a,ba,ba,bgcdgcdgcd
带入原式,得gcd(xa−1,xb−1)=gcd(x0−1,xgcd(a,b)−1)gcd(x^a-1,x^b-1)=gcd(x^0-1,x^{gcd(a,b)}-1)gcd(xa1,xb1)=gcd(x01,xgcd(a,b)1)
就是gcd(xa−1,xb−1)=xgcd(a,b)−1gcd(x^a-1,x^b-1)=x^{gcd(a,b)}-1gcd(xa1,xb1)=xgcd(a,b)1
于是就证完了,题目就可以转化为 [1,n][1,n][1,n] 中每对数的 gcdgcdgcd 对答案的贡献
首先可以想到的思路是枚举 d=gcd(a,b)d=gcd(a,b)d=gcd(a,b),有序对(a,b)(a,b)(a,b)对数就是∑i=1n/dφ(i)\sum_{i=1}^{n/d}\varphi(i)i=1n/dφ(i)(这就相当于找出所有互质的数再同时乘上 gcdgcdgcd),可以把 φ(i)\varphi(i)φ(i) 前缀和预处理出来。题目求的是无序对,就乘2再把a,ba,ba,b相同的情况减掉(因为a,b相同的情况算了两次)。
我就只想到这儿了,但到这里还是O(300∗n)O(300*n)O(300n),虽然看起来还挺优但肯定过不了。
其实只差最后一步了,看到 n/dn/dn/d 就弄个分块嘛,然后还有个等差数列求和,可是偏偏这步没想出来QAQ太菜了QAQ
还有需要注意的一点是特判x=1x=1x=1的情况,还有xgcd(a,b)−1x^{gcd(a,b)}-1xgcd(a,b)1里的那个1别忘了减。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1000001;
const int p=1e9+7;
int T,x,n;
int phi[N];
bool b[N];

void read(int &x){
	char ch=getchar();x=0;
	for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
}

ll power(int a,int n){
	ll ans=1;
	for(ll sum=a;n;sum=sum*sum%p,n>>=1) if (n&1) ans=ans*sum%p;
	return ans;
}

void Add(ll &x,int y){
	x+=y;
	while(x<0) x+=p;
	while(x>=p) x-=p;
}

void Add(int &x,int y){
	x+=y;
	while(x<0) x+=p;
	while(x>=p) x-=p;
}

void init(){
	for(int i=1;i<N;i++) phi[i]=i;
	for(int i=2;i<N;i++)
	 if (!b[i]){
	 	phi[i]=i-1;
	 	for(int j=2;i*j<N;j++)
	 	 b[i*j]=1,phi[i*j]=phi[i*j]/i*(i-1);
	 }
	for(int i=2;i<N;i++) Add(phi[i],phi[i-1]);
}

int main(){
	read(T);
	init();
	for(int o=1;o<=T;o++){
		read(x);read(n);
		if (x==1){ printf("0\n");continue; }
		ll ans=0,inv=power(x-1,p-2);
		for(int i=1,j=1;i<=n;i=j+1){
			j=n/(n/i);
			int w=n/i;
			ll v=power(x,i)*(power(x,j-i+1)-1)%p*inv%p*phi[w]%p*2%p;
			Add(ans,(int)v);
			//cout<<i<<' '<<j<<' '<<power(x,i)<<' '<<(power(x,j-i+1)-1)*inv%p<<endl;
		}
		Add(ans,-1LL*n*n%p);
		ans=(ans-(power(x,n)-1)*x%p*inv%p)%p;
		printf("%lld\n",(ans+p)%p);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值