题意:有一个长度为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;
}