POJ 2154 Color
题目链接
题解
对于一个n元素环染色,先考虑旋转,置换的总数是n个
旋转k个元素后构成的循环数,即轮换数为\(gcd(k,n)\)
根据polay定理,方案数为$$\dfrac{1}{n}\sum_{k=1}nn$$
对与于这个式子可以化为
\[\dfrac{1}{n} \sum_{d|n}n^d\sum_{k=1}^{n}[gcd(k,n)==d]
\]
\[\sum_{d|n}n^{d-1}\sum_{k=1}^{n}[gcd(\dfrac{k}{d},\dfrac{n}{d})==1]
\]
\[\sum_{d|n}n^{d-1}\sum_{k=1}^{\dfrac{n}{d}}[gcd(k,\dfrac{n}{d})==1]
\]
\[\sum_{d|n}n^{d-1}*\phi(\dfrac{n}{d})
\]
欧拉+快速幂
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
int n,p;
const int maxn = 1000000007;
inline int read() {
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
while(c<='9'&&c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
const int maxm = 1000005;
int pri[maxm],tot=0;bool vis[maxm];
int phi(int x) {
int ret=x;;
for(int i=1;pri[i]<=sqrt(x);i++)
if(x%pri[i]==0) {
ret=(ret-ret/pri[i]);
while(x%pri[i]==0)x/=pri[i];
}
if(x!=1)ret=(ret-ret/x);
return ret%p;
}
void getpre() {
for(int i=2;i<=1000000;i++) {
if(!vis[i])pri[++tot]=i;
for(int j=1;j<=tot&&pri[j]*i<=1000000;j++) {
vis[pri[j]*i]=1;
if(i%pri[j]==0)break;
}
}
}
int qpow(int n,int cnt) {
int ret=1,tmp=n;tmp%=p;
while(cnt) {
if(cnt&1) ret=(tmp*ret)%p;
tmp=tmp*tmp%p;
cnt>>=1;
}
return ret;
}
void dec(int n) {
int ans=0;
for(int i=1;i*i<=n;++i) {
if(i*i==n)ans=(ans+phi(i)*qpow(n,i-1))%p;
else if(!(n%i)) {
ans=(ans+qpow(n,i-1)*phi(n/i)+qpow(n,n/i-1)*phi(i))%p;
}
}
printf("%d\n",ans);
}
int main() {
getpre();
int cnt=read();
// for(int i=1;i<=cnt;++i) {
// printf("%d ",phi[i]);
// }
for(;cnt--;) {
n=read(),p=read();
dec(n);
}
return 0;
}