poj 2154 Color Polya定理 歐拉函數優化

題意:有一個長度爲n的項鍊,項鍊上每顆鑽石有n種染色方案,問有多少種方案


思路:置換都考慮用Polya定理做,但是會達到o(n)級別,這裏n太大,會超時。可以換個思路,一般做法是i從1枚舉到n,求每一個gcd(i, n),可以看到一個性質是每一個   gcd(i, n)都是n的約數,而n的約數是有限的,也就是10^9個gcd(i, n)會有大量的重複,那麼我們可以枚舉每一個約數。

例如d = gcd(k, n),顯然k = d * t,d = gcd(k, n) = gcd(d * t, n) = d * gcd(t, n / d)。明顯,gcd(t, n / d)必然等於1,那麼求有多少個gcd(k, n)就是求n / d的歐拉函數是多大。這道題有個地方要注意下,當一個數比較大時,它的歐拉函數也會很大,所以要記得取模。還有個可以優化的地方,可以事先打印出質因子或者打印出n的質因子,這樣做歐拉函數的複雜度能再一步減少。並且項鍊翻轉後不是等價的,而手鐲是翻轉後是等價的,所以下面置換沒有考慮翻轉

感覺這題真的挺好的,考了數論基礎的知識,但要對知識比較熟悉才能ac


題目鏈接:http://poj.org/problem?id=2154


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;

vector<int> divisor(int n)//打印出n的所有因子
{
	vector<int> res;
	int m = (int)floor(sqrt(n * 1.0) + 0.5);
	for(int i = 1; i <= m; i++)
	{
		if(n % i == 0)
		{
			res.push_back(i);
			if(i != n / i) res.push_back(n / i);
		}
	}
	return res;
}

vector<int> prime_factor(int n)//打印出n的所有質因子
{
	vector<int> res;
	int m = (int)floor(sqrt(n * 1.0) + 0.5); 
	for(int i = 2; i <= m; i++)
	{
		if(n % i == 0)
		{
			res.push_back(i);
			while(n % i == 0) n /= i;
		}
	}
	if(n > 1) res.push_back(n);
	return res;
}

int quick_pow(int x, int n, int mod)
{
	int res = 1;
	x %= mod;//這裏可以先處理下,當然這裏因爲mod比較小的情況,如果mod比較大,那麼要開longlong或快速乘
	while(n)
	{
		if(n & 1) res = res * x % mod;
		x = x * x % mod;
		n >>= 1;
	}
	return res;
}

int euler_phi(int n, const vector<int> &prime)
{
	int m = prime.size();
	int res = n;
	for(int i = 0; i < m; 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 polya(int n, int mod)
{
	vector<int> divs = divisor(n);
	vector<int> primes = prime_factor(n);//可以預先處理n的質因子
	int ans = 0;
	int m = divs.size();
	for(int i = 0; i < m; i++)
	{
		int euler = euler_phi(divs[i], primes) % mod;//這裏要注意取模,當n大時,他的歐拉函數有也會很大
		ans += euler * quick_pow(n, n / divs[i] - 1, mod) % mod;
		ans %= mod;
	}
	return ans % mod;
}

int main()
{
	//freopen("/home/zyh/duipai/data.in", "r", stdin);
	//freopen("/home/zyh/duipai/out1.out", "w", stdout);
	int t, n, mod;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d%d", &n, &mod);
		int ans = polya(n, mod);
		printf("%d\n", ans);
	}
	return 0;
}


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