POJ 2154 Color

题目链接

POJ 2154 color

题解

对于一个n元素环染色,先考虑旋转,置换的总数是n个
旋转k个元素后构成的循环数,即轮换数为(gcd(k,n))
根据polay定理,方案数为$$dfrac{1}{n}sum_{k=1}^nn^{gcd(k,n)}$$
对与于这个式子可以化为

[dfrac{1}{n} sum_{d|n}n^dsum_{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
",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;
}


原文地址:https://www.cnblogs.com/sssy/p/8434616.html