首页 > 代码库 > [ACM] POJ 2154 Color (Polya计数优化,欧拉函数)
[ACM] POJ 2154 Color (Polya计数优化,欧拉函数)
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 7630 | Accepted: 2507 |
Description
You only need to output the answer module a given number P.
Input
Output
Sample Input
5 1 30000 2 30000 3 30000 4 30000 5 30000
Sample Output
1 3 11 70 629
Source
转载:http://blog.csdn.net/tsaid/article/details/7366708
题意:给出两个整数n和p,代表n个珠子,n种颜色,要求不同的项链数,翻转置换不考虑。结果模p.
题解:
我们知道gcd(i,n)表示了循环节的个数。例如gcd(2,6) = 2, 它的具体过程为:[1,3,5] [2,4,6]
对于任意一个循环置换,他所有循环节的长度为 n / gcd(i,n),在上面的例子中: 循环节长度 = 6 / gcd(2,6) = 3
为了方便说明,用L表示循环节的长度,显然 L | n
如果我们枚举L,求出对于每一个L有多少个i, 使得 L = n / gcd (i,n), 那么我们实际上也得到了循环节个数为 n / L 的置换个数。
将L = n / gcd (i,n)转换一下得到:n / L = gcd(i,n )
设 cnt = n / L = gcd(i, n) 注:cnt表示循环节个数,L表示每一个循环节的长度
因为 cnt | i, 所以可令 i = cnt * t; ( 因为0 <= i < n, 所以0 <= t < n / cnt = L )
又因为 cnt = n / L, 所以 n = cnt * L;
则 gcd ( i, n ) = gcd ( cnt*t, cnt*L ) = cnt; ①
可知当 gcd ( t, L ) = 1 时 ① 式成立。
由于 gcd ( t, L ) = 1 的个数就是 Euler(L)的个数,
所以我们可以得到结论:循环节个数为n/L的置换有Euler(L)个。
代码:
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; bool isprime[50001]; int prime[50001]; int len=0;; int n,p; void sieve() { for(int i=0;i<=50000;i++) isprime[i]=1; isprime[0]=isprime[1]=0; for(int i=2;i<=50000;i++) { if(isprime[i]) { prime[len++]=i; for(int j=2*i;j<=50000;j+=i) isprime[j]=0; } } } int euler(int n) { int res=n; for(int i=0;i<len&&prime[i]*prime[i]<=n;i++) { if(n%prime[i]==0) { res=res/prime[i]*(prime[i]-1); while(n%prime[i]==0) n/=prime[i]; } } if(n>1) res=res/n*(n-1); return res; } int pow(int p,int n,int mod) { int ans=1; p=p%mod; while(n) { if(n&1) ans=ans*p%mod; p=p*p%mod; n/=2; } return ans; } int main() { sieve(); int t; scanf("%d",&t); while(t--) { int ans=0; scanf("%d%d",&n,&p); for(int i=1;i*i<=n;i++) if(n%i==0) { ans=(ans+euler(i)%p*pow(n,n/i-1,p))%p;//注意取余的位置, euler(i)%p不取余就WA if(i*i==n)//只要一个i就可以了 break; ans=(ans+euler(n/i)%p*pow(n,i-1,p))%p; } printf("%d\n",ans); } return 0; }