題意:有一個長度爲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;
}