题目:http://poj.org/problem?id=2154
置换的第二道题!
需要优化!式子是ans=∑n^gcd(i,n)/n (i∈1~n),可以枚举gcd=g,则有phi( n/g )个数与n的gcd是g。
g是n的约数,成对出现,可以O(sqrt(n))枚举。用不断 /p 的log(n)做法求单个的phi。(不用专门看p是不是质数,此处可以保证一定是质数)
注意pw里的x传进去要先%mod!!!因为它是1e9级别的,一开始(x*=x)%=mod的时候会爆。
如果把n开成long long,这里传进去long long,就没事了。但会TLE!!!所以还是先取模。
注意各种有除法的地方都不能随时取模。如phi,和ans/n的除法的处理。
#include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; int T,mod,n; ll ans; int phi(int a) { int ret=a; for(int i=2;i*i<=a;i++) if(a%i==0) { ret=ret/i*(i-1); // ret-=ret/i;//这样也行! while(a%i==0)a/=i; } // if(a!=1)ret-=ret/a; if(a!=1)ret=ret/a*(a-1);//剩下一个质因数 return ret%mod;//不要中途%mod,有除法! } ll pw(int x,int k) { ll ret=1;x%=mod;/////x是1e9的,但long long会爆! while(k){if(k&1)(ret*=x)%=mod;(x*=x)%=mod;k>>=1;}return ret; } int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&mod);ans=0; for(int i=1;i*i<=n;i++) //i是gcd的话,n/i也是gcd! { if(n%i)continue; (ans+=pw(n,i-1)*phi(n/i))%=mod; if(i*i!=n)(ans+=pw(n,n/i-1)*phi(i))%=mod; } printf("%lld\n",ans);//不要这里/n,模意义下不行。让上面的pw少乘一个n } return 0; }