標籤:blog color os 2014 for io
枚舉位移肯定逾時,對於一個位移i,我們需要的是它的迴圈個數,也就是gcd(i,n),gcd(i,n)個數肯定不會很多,因為等價於n的約數的個數。
所以我們枚舉n的約數,對於一個約數k,也就是迴圈個數為n/k這樣的個數有phi[k]種,證明網上有很多。所以答案就是 phi[k]*(pow(n,n/k)) (k是n的所有約數)
由於約數會很大所以不能打表,只能單個算。
再由於最後要除以n,如果做除法就不能直接模數,所以我們在算每一次pow(n,n/k)的時候,都少乘一個n,這樣就相當於除法了。
#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>#include<cstring>using namespace std;const int N=1000000;int quickpow(int m,int n,int k){ int ans=1; while(n) { if(n&1) ans=(ans*m)%k; n=(n>>1); m=(m*m)%k; } return ans;}bool a[N];int prim[N];int pp[N];void Prime(){ memset(a, 0, sizeof(a)); int num = 0, i, j; pp[1]=1; for(i = 2; i < N; ++i) { if(!(a[i])) prim[num++]=pp[i]=i; for(j = 0; (j<num && i*prim[j]<N); ++j) { pp[i*prim[j]]=prim[j]; a[i*prim[j]] = 1; if(!(i%prim[j])) break; } }}int phi(int x){ int i,j; int num = x; for(i = 0; prim[i]*prim[i] <= x; i++) { if(x % prim[i] == 0) { num = (num/prim[i])*(prim[i]-1); while(x % prim[i] == 0) { x = x / prim[i]; } } } if(x != 1) num = (num/x)*(x-1); return num;}int main(){ Prime(); int cas,n,p; scanf("%d",&cas); while(cas--) { int ans=0; scanf("%d%d",&n,&p); for(int l=1;l*l<=n;l++) { if(n%l==0) { if(l*l==n) { ans+=phi(l)%p*quickpow(n%p,l-1,p); ans%=p; break; } ans+=phi(l)%p*quickpow(n%p,n/l-1,p); ans+=phi(n/l)%p*quickpow(n%p,l-1,p); ans%=p; } } printf("%d\n",ans); } return 0;}