poj 2154 Color

题目链接

n种珠子n种颜色,要求不同的涂色方案,考虑旋转置换,不考虑翻转置换,结果模除p。

置换考虑用Polya定理写(Burnside引理与Polya定理知识点),复杂度达到O(n),这里的n太大,直接用会超时。

cnt = gcd(k,n)  表示第k种置换的循环节个数

L = n / cnt  表示每个循环节的长度 

则 k = cnt * t

cnt = gcd(cnt * t , n)=cnt * gcd(t , n/cnt)  ==> gcd (t , n/cnt)=1  ==> gcd (t , L)=1

所以求有多少个gcd(k,n)就是求n / cnt的欧拉函数多大。欧拉是和质因子有关,可以预先质因子打表

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+9;

int p[N],prim[N],len;  //p存最小质因子,prim存质数 
int eul[N];

void get_prime(){   //素数打表 
	len=0;
	memset(p,0,sizeof(p));
	for(int i=2;i<N;i++){
		if(!p[i])
			prim[len++]=i;
		for(int j=0;j<len&&i*prim[j]<N&&(prim[j]<=p[i]||p[i]==0);j++){
			p[i*prim[j]]=prim[j];   //找合数的质因子 

		}
		
//		printf("p[%d]=%d\n",i,p[i]);  //i为质数,p[i]为零 ;  i为合数,p[i]为其最小质因子 
	}
	
//	for(int i=0;i<20;i++)
//		printf("prim[%d]=%d\n",i,prim[i]);
} 

void Euler(){  //欧拉打表 
	eul[1]=1;
	
	for(int i=2;i<N;i++){
		if(!p[i])          //质数的欧拉是质数-1 
			eul[i]=i-1;
		else{
			int k=i/p[i];   
			if(k%p[i]==0)  //k存在质因子p[i]
				eul[i]=eul[k]*p[i];
			else     
				eul[i]=eul[k]*(p[i]-1); 
				
		}
	}
} 

int Euler(int n,int mod){  //n的欧拉 
	if(n<N)
		return eul[n]%mod;
		
	int ans=n;
	
	for(int i=0;i<len&&prim[i]*prim[i]<=n;i++){
		if(n%prim[i]==0){
			ans=ans-ans/prim[i];
			while(n%prim[i]==0)
				n/=prim[i];
		}
	}
	if(n>1)
		ans=ans-ans/n;
	return ans%mod;
}

int quick_pow(int a,int b,int mod){  //快速幂 
	int ans=1;
	a=a%mod;
	while(b){
		if(b&1)
			ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
} 

int Polya(int n,int mod){  //Polya定理 
	int ans=0;
	
	for(int L=1;L*L<=n;L++){
		if(n%L)
			continue;
		ans=(ans+Euler(L,mod)*quick_pow(n,n/L-1,mod))%mod;
		if(L*L==n)
			break;
		ans=(ans+Euler(n/L,mod)*quick_pow(n,L-1,mod))%mod;
	}
	return ans;
}

int main(){ 
	get_prime();
	Euler();
	int t;scanf("%d",&t);
	int n,p;
	while(t--){
		scanf("%d %d",&n,&p);
		printf("%d\n",Polya(n,p));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lidengdengter/article/details/81146827