POJ 2154 Color

POJ_2154

    首先,直接应用polya定理就可以得到ans=(N^gcd(0,N)+N^gcd(1,N)+…+N^gcd(N-1,N))/N,如果直接计算的话,即便应用快速幂,也改变不了循环计算N项和的复杂度。

    于是便不得不从gcd的值入手看是否能合并一些项达到化简的目的了。尽管一共有N项,但gcd的值一定到不了N项,因为gcd是N的约数,而一个数的约数是没有那么多的。顺着这个思路想的话,我们有没有办法更快地求得N的所有约数呢?这一点是可以在O(sqrt(N))的时间内办到的。我们只要枚举sqrt(N)以内的能够整除N的整数,就可以找到N所有的约数,如果同时我们可以O(1)的时间求出指数为该约数的项一共有多少的话,那么这个题整体就是O(sqrt(N))的复杂度了。

    那么这时就还剩一个问题没有解决了,对于N的任意一个约数,我们不妨设其值为d,指数为d的项一共有多少呢?也就是说一共有多少个满足gcd(x,N)=d的x呢?实际上,由于N和x的最大公约数是d,那么x/d和N/d必然互素,同时x<N,那么x的数量也就是N/d的欧拉函数值。

    最后还有一个事情,就是/N的问题,由于和式的每一项都是N^gcd,于是每项直接变成N^(gcd-1)即可。

    解决完上述几个问题之后,程序就不难写了,不过由于这个题时限卡得比较紧,所以求欧拉函数时要用素数表优化,同时筛素数的时候又可以把一些比较小的数的欧拉函数顺便也求出来。

#include<stdio.h>
#include<string.h>
#include<math.h>
#define MAXM 100000
int N, M, P, phi[MAXM], prime[MAXM], X;
void prepare()
{
int i, j;
M = (int)sqrt(1000000000 + 0.5);
memset(phi, 0, sizeof(phi[0]) * (M + 1));
phi[1] = 1;
X = 0;
for(i = 2; i <= M; i ++)
if(!phi[i])
{
prime[X ++] = i;
for(j = i; j <= M; j += i)
{
if(!phi[j])
phi[j] = j;
phi[j] = phi[j] / i * (i - 1);
}
}
}
int getphi(int n)
{
int i, j, k, cnt = n;
if(n <= M)
return phi[n];
for(i = 0; i < X && prime[i] * prime[i] <= n; i ++)
if(n % prime[i] == 0)
{
cnt = cnt / prime[i] * (prime[i] - 1);
while(n % prime[i] == 0)
n /= prime[i];
}
if(n > 1)
cnt = cnt / n * (n - 1);
return cnt;
}
long long int pow_mod(int a, int n)
{
long long int ans;
if(n == 0)
return 1 % P;
if(n == 1)
return a % P;
ans = pow_mod(a, n / 2);
ans = ans * ans % P;
return n % 2 ? ans * a % P : ans;
}
void solve()
{
int i, j, k, ans = 0;
for(i = 1; i * i < N; i ++)
if(N % i == 0)
ans = ((getphi(N / i) % P) * pow_mod(N, i - 1) + (getphi(i) % P) * pow_mod(N, N / i - 1) + ans) % P;
if(i * i == N)
ans = ((getphi(i) % P) * pow_mod(N, i - 1) + ans) % P;
printf("%d\n", ans);
}
int main()
{
int t;
prepare();
scanf("%d", &t);
while(t --)
{
scanf("%d%d", &N, &P);
solve();
}
return 0;
}


你可能感兴趣的:(color)