POJ 2154 Color Polya定理+欧拉函数

/*
题目要求:给出两个整数n和p,代表n个珠子,n种颜色,要求不同的项链数,并对结果mod(p)处理。

置换只有旋转一种方式,那么共有n个置换
基本知识:环的个数为gcd(n , i) , 长度L=n / gcd(n , i)   其中 i 为转的位子数
普通求法: ∑n^( gcd(n,i) )  0<=i<n  复杂度过高
优化:枚举环的长度L
枚举优化: L可以从1取到sqrt(n) ,因为L|n , n/L | n
对于每个L,我们再看有几个i满足条件
n/L = gcd(n , i) 
那么令 a=n/L = gcd(n , i) , 再设i = at
那么当且仅当gcd(L,t)=1时候,才有gcd(n,i) = a
显然满足条件的i的个数就是t的个数也就是phi(L)
那么最后统计一下就是 ∑(phi(L) * N^(L-1) ) % p  (L即枚举值) 
*/
#include<iostream>
#include <stdlib.h>
#include <algorithm>
#include <stdio.h>
#include<vector>
using namespace std;
const int MAXN=1000000000;
int isprime[50001];
int prime[8001];
int num,n,p;
void getprime()
{
	num=0;
	for(int i=2;i<=50000;i++)if(!isprime[i])
	{
		prime[num++]=i;
		for(int j=1;j*i<=50000;j++)
		{
			isprime[i*j]=1;
		}
	}
}
int euler(int x)
{
	int res=x;
	for(int i=0;i<num&&prime[i]*prime[i]<=x;i++)
	{
		if(x%prime[i]==0)
		{
			res=res/prime[i]*(prime[i]-1);
			while(x%prime[i]==0)
			{
				x/=prime[i];
			}
		}
	}
	if(x>1) res=res/x*(x-1);
	return res;
}
int expmod(int a,int b,int mod)
{
    int ret=1;
    a=a%mod;
    while(b>0)
    {
        if(b&1)ret=(ret*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ret;
}

int main()
{
	int Case;
	getprime();
	scanf("%d",&Case);
	while(Case--)
	{
		scanf("%d%d",&n,&p);
		int ans=0,i;
		for(i=1;i*i<n;i++)if(n%i==0)
		{
			ans=(ans+euler(i)%p*expmod(n,n/i-1,p)+euler(n/i)%p*expmod(n,i-1,p))%p;;//这里的i-1代表已经除以整个置换数n了,原本是expmod(n,i),最后要除以n的,
		}
		if(i*i==n)
		ans=(ans+euler(i)*expmod(n,i-1,p))%p;
		cout<<ans<<endl;
	}
    return 0;
}

你可能感兴趣的:(优化,ini)