POJ2154(Pólya定理與歐拉函數優化)

題意:將正n邊形的n個頂點用n種顏色染色,問有多少種方案(答案mod p,且可由旋轉互相得到的算一種)

可以看一下挑戰程序,裏面有一個更加泛用的推導。有兩點需要注意的地方

1.題目中n爲10^9,一開始被嚇到了,覺得如果d=1時,n=10^9,那不是就意味着要求那麼大的歐拉函數了麼,這素數一稿,時間絕對超了,後來發現其實只要獲得根號n的大小素數就行了,除掉前面素數之後如果還大於1,那必定就是素數了,把這個素數也算進歐拉函數計算裏

2.歐拉函數參數爲1時取值爲0,即當n==d,即k==n時,不存在這種情況,因0<=k<=n-1

先說說Pólya定理

設Q是n個對象的一個置換羣,用m種顏色塗染這n個對象,一個對象塗任意一種顏色,則在Q作用下不等價的方案數爲:   

|Q|爲置換羣中置換的個數,爲將置換q表示成不相雜的輪換的個數,其中包括單輪換,m爲顏色數。

 

分析可以知道本題方案的表達式爲:

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 40000
using namespace std;
bool prime[MAXN];
int pr[MAXN],cnt,n,mod,phi[MAXN];
void isprime()
{
	cnt=0;
	memset(prime,false,sizeof(prime));
	for(int i=2;i<=MAXN-10;i++)
	{
		if(!prime[i])
		{
			pr[cnt++]=i;
			for(int j=i+i;j<=MAXN-10;j+=i)
			{
				prime[j]=true;
			}
		}
	}
}
int quick_mul(int n,int d)
{
	int ans=1;
	n%=mod;
	while(d)
	{
		if(d&1)
		{
			ans=ans*n%mod;
			d--;
		}
		d>>=1;
		n=n*n%mod;
	}
	return ans;
}
int eular(int x)
{
	int ans=x;
	for(int i=0;pr[i]*pr[i]<=x;i++)//所有大小隻要設平方根n即可 
	{
		//cout<<endl<<x<<" "<<pr[i]<<endl;
		if(x%pr[i]==0)
		{
			ans=ans-ans/pr[i];
			while(x%pr[i]==0) 
			{
				x/=pr[i];
				//cout<<1;
		    }
		 } 
	}
	if(x>1)
		ans=ans-ans/x;
	return ans%mod;
}
int main()
{
	//FILE *fp=fopen("t.txt","r");
	int x;
	scanf("%d",&x);
	isprime();
	while(x--)
	{
		int ans=0;
		scanf("%d%d",&n,&mod);
		for(int i=1;i*i<=n;i++)
		{
			//cout<<1;
			if(i*i==n)
			{
				ans+=quick_mul(n,i-1)*eular(i)%mod;
				//cout<<i;
				ans%=mod;
			}else if(n%i==0)
			{
				ans+=quick_mul(n,n/i-1)*eular(i)%mod+quick_mul(n,i-1)*eular(n/i)%mod;
				ans%=mod;
			}
		}
		printf("%d\n",ans%mod);
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章