题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3875
Every scenario consists of a single line containing two integers n and c separated by a space.
if the difference is the multiple of c print "yes",otherwise print "no"
这个题应该是数论中比较综合的一道题目。
做这个题需要知道大数的质因数分解方法(米勒罗宾测试),欧拉函数的积性函数性质,除法取模的方法等等
这个题由于数据量很大,所以要分解质因数必须要用到米勒罗宾的随机算法,快速分解。
然后后面就是公式推导了。
首先看gcd sum的快速求解方法。
这里我们假设f(x)=sigma(gcd(i,n))
由于这是一个积性函数那么必然有:
f(x)=f(p1^k1)*f(p2^k2)*f(p3^k3)*....*f(pn^kn)
单单这样,似乎规律并不明显,不过第一步我们已经明确。就是将n进行质因数分解
然后接下来看形如f(p^k)有没有什么规律
注意到p^k的约数只有1,p,p^2,p^3.....p^k
那么必然的,f(p^k)=1*eular(p^k)+p*eular(p^(k-1))+....+p^k*eular(1)//这个题的基础是那个电子科大的数论中的一道题目演变而来的。这个公式不在附加说明。
注意到:eular(p^k)=p^k-p^(k-1)这个公式来自于数论概论,不过利用欧拉函数性质也很好推导。
把这个式子代入f(p^k)得到
f(p^k)=k*(p^k-p^(k-1))+p^k
于是对于gcd的和我们可以再分解完因式之后快速计算出来。
接下来是比较困难的lcm求和
有一个较为朴素的公式,相信大家都能推导出来:
ans=sigma(n/d*eular(n/d)*n/2)
n/2是每一个与n的最大公约数为d的数的平均数。(理论依据是所有小于n的且与n互质的数的和为(1+2+3+..+n)/2,详细内容可以查阅相关资料)
然后说说这个式子怎么转化吧。
其实,n/d*eular(n/d)=d*eular(d)。原因就是d会出现的数字,n/d也一定会出现。
举个例子:n=6,那么d=1,2,3,6 n/d=6,3,2,1
完全一样的说~
然后于是ans=sigma(d*eular(d)*n/2)
这里说说当d=1的时候,这个时候是一个特殊情况。
因为我们这个时候平均数事实上是n,而非n/2
所以式子需要修正:ans=sigma(d*eular(d)*n/2+n/2)
ans=n*sigma(d*eular(d)+1)
注意到上面两个式子在数学上面并不相等,但是由于这里,除号是一个整除法,所以这里,其实两个式子是相等的(至少计算机算出来是一样的)
然后这里之后,我们还可以继续化简
假设f(x)=x*eular(x)
这个也是一个积性函数
那么f(n)=f(p1^k1)*f(p2*k2)*f(p3^k3)....*f(pn*kn)
然后同样的,我们单独考虑
f(p^k)=1*1+p*eular(p)+p^2*eular(p^2)+....+p^k*eular(p^k)
继续化简:(等比数列求和)
得到:f(p^k)=(p^2k+1)/(p+1)
这样我们就也可以在分解完素因数之后快速求的lcm sum
最后注意一点由于lcm sum的公式里面有一个1/2
所以我们必须把原来的数扩大两倍,然后再把c扩大两倍,取余的结果最后除以2就是原来的答案
详细见代码:
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> using namespace std; typedef unsigned __int64 u64; const int MAX=100; u64 f0[100],f1[100],ff,n,tmp,ret,ret1,p,pp; u64 myrandom() { u64 a; a=rand(); a*=rand(); a*=rand(); a*=rand(); return a; } u64 mulmod(u64 a,u64 b,u64 c) { u64 ret=0; while(b) { if(b&1) { ret+=a; if(ret>c) ret-=c; } a<<=1; if(a>c) a-=c; b>>=1; } return ret; } u64 powmod(u64 a,u64 b,u64 c) { u64 ret=1; while(b) { if(b&1) ret=mulmod(ret,a,c); a=mulmod(a,a,c); b>>=1; } return ret; } int miller(u64 base,u64 n) { u64 m=n-1,k=0; while(m%2==0) { m>>=1; k++; } if(powmod(base,m,n)==1) return 1; for(int i=0;i<k;i++) { if(powmod(base,m<<i,n)==n-1) return 1; } return 0; } int Miller_Rabin(u64 n) { for(int i=2;i<100;++i) if(n%i==0) if(n==i) return 1; else return 0; for( i=0;i<MAX;++i) { u64 base=myrandom()%(n-1)+1; if(!miller(base,n)) return 0; } return 1; } u64 gcd(u64 a, u64 b) { if(b==0) return a; return gcd(b,a%b); } u64 f(u64 a,u64 b) { return (mulmod(a,a,b)+1)%b; } u64 Pollard_Rho(u64 n) { if(n<=2) return 0; for(u64 i=2;i<100;++i) if(n%i==0) return i; u64 p,x,xx; for( i=1;i<MAX;i ++) { x=myrandom()%n; xx=f(x,n); p=gcd((xx+n-x)%n,n); while(p==1) { x=f(x,n); xx=f(f(xx,n),n); p=gcd((xx+n-x)%n,n)%n; } if(p) return p; } return 0; } u64 Prime(u64 a) { if(Miller_Rabin(a)) return 0; u64 t=Pollard_Rho(a); u64 p=Prime(t); if(p) return p; return t; } int main() { int tt; scanf("%d",&tt); while(tt--) { scanf("%I64d %I64d",&n,&p); pp=p*2; u64 old=n%pp; ret=1,ret1=1,ff=0; while(n>1) { if(Miller_Rabin(n)) break; tmp=Prime(n); f0[ff]=tmp; f1[ff]=1; n/=tmp; while(n%tmp==0) { n/=tmp; f1[ff]++; } ff++; } if(n>0) { f0[ff]=n; f1[ff++]=1; } for(int i=0;i<ff;++i) { u64 tmp=1; for(int j=0;j<f1[i];++j) tmp=tmp*f0[i]; ret1=ret1*(f1[i]*(tmp-tmp/f0[i])+tmp); ret1=ret1%p; tmp=1; for(j=0;j<2*f1[i]+1;++j) tmp=tmp*f0[i]; ret=ret*((tmp+1)/(1+f0[i])); ret=ret%pp; } ret=((old*(ret+1))%pp)/2; if(ret==ret1) printf("yes\n"); else printf("no\n"); } return 0; }