题意:n种颜色的珠子可组成多少种长度为n的项链?(旋转算一种,不考虑翻转)n<1000000000,结果对p取余,p<30000
分析:本题数据规模很大,若枚举旋转的长度会超时。
假设旋转 i 步,则循环节个数为gcd( i, n ) (证明在这里) ,循环节长度 L = n / gcd(i, n). 则 L | n,可以枚举L,并计算gcd(i, n) = n / L的 i 的个数。
设gcd(i, n) = t, i = kt,n = Lt, gcd(kt, Lt) = t 等价于 gcd(k, L)=1,这样的 k 有 φ( L )个, 即 i 有 φ( L )个。
于是答案为 (1/n) ∑ ( φ(L)* n^(n/L) ) mod p.其中L|n。由于(1/n)不方便对p取余,可以和n^(n/L)抵消,所以答案为∑ ( φ(L)* n^(n/L - 1) ) mod p.
优化:
1、L|n,则 n/L | n,所以L只需要枚举到 sqrt( n )。
2、预处理n,得到 n 的所有素因子pr [i] 及其个数cnt[i]。枚举L的时候,直接枚举每个素因子的个数,乘积即为L。
int pri[]={2,3,5,7,11,13}//这里要打表到31627,由于太多了,这里就不贴了 const int m = 35; int pr[m], cnt[m], len; int n, sqr, p, ans; void init(int n){ len = ans = 0; sqr = sqrt(n*1.0); memset(cnt, 0, sizeof cnt); for(int i = 0; pri[i] * pri[i] <= n; i++) if(n % pri[i] == 0){ //记录n的素因子及其个数 pr[len] = pri[i]; while(n % pri[i] == 0) { cnt[len]++; n /= pri[i]; } len++; } if(n>1) {pr[len] = n; cnt[len++] = 1;} } int modexp(int a, int b, int n) {//快速幂 LL ans = 1, t = a; while(b) { if(b & 1) ans = ans * t % n; t = t * t % n; b >>= 1; } return (int)ans; } int euler1(int n){//求φ(n) int res = n; for(int i = 0; pri[i] * pri[i] <= n; i++) if(n % pri[i] == 0){ res = res /pri[i] * ( pri[i] - 1 ); while(n % pri[i] == 0) n /= pri[i]; } if(n>1) res=res/n*(n-1);//n是质因数 return res % p; } void solve(int i, int L, int count){//枚举到第i个因子,已有乘积L,第i-1个因子的个数count //count若为0,表示第i-1个因子没选,即L已计算过,这里不要重复计算 if(count){//L没被计算过 int t = n / L; int pow = t - 1; int t1 = modexp( n, pow, p ); int t2 = euler1( L ); ans = ( ans + t1 * t2 ) % p; if(L * L != n){//转变L 和 n/L pow = L - 1; t1 = modexp( n, pow, p ); t2 = euler1( t ); ans = ( ans + t1 * t2 ) % p; } } if(i==len) return ; solve(i+1, L, 0);//第i个因子没选 int r = 1; FOE(k, 1, cnt[i]){ r *= pr[i]; int temp = L*r; if(temp <= sqr) solve(i+1, L*r, k);//第i个因子选了k个 } } int main(){ #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); #endif int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&p); init(n); solve(0, 1, 1); printf("%d\n", ans); } return 0; }