题目要求:给出两个整数n和p,代表n个珠子,n种颜色,要求不同的项链数,并对结果mod(p)处理。
此题本人不会做啊,只能找解题报告了,要用到欧拉函数x是n约数, 最后的答案是 (1/n * ∑Euler(n/x)(n^x)) % p x= 1
由于只关心gcd(N,i),考虑N的某个约数m在1...N的gcd中出现了几次。
gcd(N,i)==m ==> gcd(N/m,i/m)==1
显然就是欧拉函数 phi(N/m)
那么合式变为 sigma( phi(m)*N^(m) )/N
也就是 sigma( phi(m)*N^(m-1) )
分析:这题和1286,2409 都有点类似,不同之处在于,只考虑旋转,不考虑翻转;因此相对前面两个题目应该说是更简单,但一看数据范围,就不是这么回事了,1286和2409完全可以直接循环处理,但这题目n最大达100000000,显然会TLE,故需寻求更佳的解决方案。
可以用欧拉函数进行优化,或者用Mobius反演定理进行优化。下面讲解一下用欧拉优化的方法:
旋转:顺时针旋转i格的置换中,循环的个数为gcd(i,n),每个循环的长度为n/gcd(i,n)。
如果枚举旋转的格数i,复杂度显然较高。有没有好方法呢?可以不枚举i,反过来枚举L。
由于L|N,枚举了L,再计算有多少个i使得0<=i<=n-1并且L=gcd(i, n)。即gcd(i,n)=n/L。
不妨设a=n/L=gcd(i, n),
不妨设i=a*t则当且仅当gcd(L,t)=1时
Gcd(i,n)=gcd(a*L,a*t)=a。
因为0<=i<n,所以0<=t<n/a=L.
所以满足这个条件的t的个数为Euler(L).
现在结果已经很明显了。Ans=∑(Euler(L)*(n.^(L-1)))%p。(L为符合上面假设条件的所有数).
复杂度分析:线性筛选素数,线性筛选欧拉函数因子,枚举L,这些都是在线性的复杂度内完成的。
#include<iostream> #include<cmath> using namespace std; //1872ms #define M 50005 int prime[10000]; int boo[M]; int fun(int n,int c,int p)//求n的c次方mod p { int i,x; int a[100]; a[0]=0; while (c) { a[0]++; a[a[0]]=c%2; c=c/2; } x=1; for (i=a[0];i>=1;i--) { if (a[i]==1) x=(((x*x)%p)*n)%p; else x=(x*x)%p; } return x; } int euler(int x)//求欧拉函数 { int i, res = x; for(i=2; i<(int)sqrt(x*1.0)+1; i++) { if(x%i==0) { res = res / i * (i-1); while(x%i==0) x /= i; } } if(x > 1) res = res / x * (x-1); return res; } /* int euler(int n)//求欧拉函数 { int i,l,t; l=n; for (i=1;i<=prime[0];i++) { t=0; while (l%prime[i]==0) { t++; l=l/prime[i]; } if (t>0) n=n/prime[i]*(prime[i]-1); if (l==1) break; if (l/prime[i]<prime[i]) { n=n/l*(l-1); break; } } return n; }*/ void Prime() { int num,k,i; for(i=2; i<=50000; i+=2) boo[i] = 0; for(i=2; i<=300; i++) for(k=i*i; k<=50000; k+=i) boo[k] = 1; num = 0; for(i=2; i<=50000; i++) if(boo[i] == 0) prime[num++] = i; } /* void Prime()//求素数 { int i,j,k,t; for (i=2;i<=50000;i+=2) boo[i]=0; for (i=1;i<=50000;i+=2) boo[i]=1; boo[1]=0; boo[2]=1; for (i=3;i<=300;i+=2) { if (boo[i]) { k=2*i; t=i+k; while (t<=50000) { boo[t]=0; t=t+k; } } } k=1; prime[1]=2; for (i=3;i<=50000;i+=2) { if (boo[i]==1) { k++; prime[k]=i; } } prime[0]=k; }*/ int main() { int t, n, p,i,ans; scanf("%d",&t); Prime(); while(t-- && scanf("%d%d",&n,&p)) { ans = 0; for(i=1; i*i<=n; i++) //枚举长度 { if(n % i == 0) //有长度为i的循环,就会有长度为n/i的循环; { ans = (ans + euler(n/i)%p * fun(n%p, i-1, p)) % p; if(i != n/i) //枚举循环长度i,找出相应的i的个数:gcd(n,i)=n/i; ans = (ans + euler(i)%p * fun(n%p, n/i-1, p)) % p; } } printf("%d/n",ans); } return 0; }