POJ2154(Pólya定理与欧拉函数优化)

题意:将正n边形的n个顶点用n种颜色染色,问有多少种方案(答案mod p,且可由旋转互相得到的算一种)

可以看一下挑战程序,里面有一个更加泛用的推导。有两点需要注意的地方

1.题目中n为10^9,一开始被吓到了,觉得如果d=1时,n=10^9,那不是就意味着要求那么大的欧拉函数了么,这素数一稿,时间绝对超了,后来发现其实只要获得根号n的大小素数就行了,除掉前面素数之后如果还大于1,那必定就是素数了,把这个素数也算进欧拉函数计算里

2.欧拉函数参数为1时取值为0,即当n==d,即k==n时,不存在这种情况,因0<=k<=n-1

先说说Pólya定理

设Q是n个对象的一个置换群,用m种颜色涂染这n个对象,一个对象涂任意一种颜色,则在Q作用下不等价的方案数为:   

|Q|为置换群中置换的个数,为将置换q表示成不相杂的轮换的个数,其中包括单轮换,m为颜色数。

 

分析可以知道本题方案的表达式为:

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 40000
using namespace std;
bool prime[MAXN];
int pr[MAXN],cnt,n,mod,phi[MAXN];
void isprime()
{
	cnt=0;
	memset(prime,false,sizeof(prime));
	for(int i=2;i<=MAXN-10;i++)
	{
		if(!prime[i])
		{
			pr[cnt++]=i;
			for(int j=i+i;j<=MAXN-10;j+=i)
			{
				prime[j]=true;
			}
		}
	}
}
int quick_mul(int n,int d)
{
	int ans=1;
	n%=mod;
	while(d)
	{
		if(d&1)
		{
			ans=ans*n%mod;
			d--;
		}
		d>>=1;
		n=n*n%mod;
	}
	return ans;
}
int eular(int x)
{
	int ans=x;
	for(int i=0;pr[i]*pr[i]<=x;i++)//所有大小只要设平方根n即可 
	{
		//cout<<endl<<x<<" "<<pr[i]<<endl;
		if(x%pr[i]==0)
		{
			ans=ans-ans/pr[i];
			while(x%pr[i]==0) 
			{
				x/=pr[i];
				//cout<<1;
		    }
		 } 
	}
	if(x>1)
		ans=ans-ans/x;
	return ans%mod;
}
int main()
{
	//FILE *fp=fopen("t.txt","r");
	int x;
	scanf("%d",&x);
	isprime();
	while(x--)
	{
		int ans=0;
		scanf("%d%d",&n,&mod);
		for(int i=1;i*i<=n;i++)
		{
			//cout<<1;
			if(i*i==n)
			{
				ans+=quick_mul(n,i-1)*eular(i)%mod;
				//cout<<i;
				ans%=mod;
			}else if(n%i==0)
			{
				ans+=quick_mul(n,n/i-1)*eular(i)%mod+quick_mul(n,i-1)*eular(n/i)%mod;
				ans%=mod;
			}
		}
		printf("%d\n",ans%mod);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值