【POJ2154】Color Pólya定理+欧拉函数

【POJ2154】Color

题意:求用\(n\)种颜色染\(n\)个珠子的项链的方案数。在旋转后相同的方案算作一种。答案对\(P\)取模。
询问次数\(\le 3500\)\(n\le 10^9,P\le 30000\)
题解:旋转i次的循环个数显然是\(gcd(i,n)\),然后套用Pólya定理。

\[ans=\frac 1 n \sum\limits_{i=1}^nn^{gcd(i,n)} \]

\[ans=\sum\limits_{i=1}^nn^{gcd(i,n)-1} \]

\[ans=\sum\limits_{i=1}^n\sum\limits_{d|n}n^{d-1}[gcd(i,n)=d] \]

\[ans=\sum\limits_{d|n}n^{d-1}\sum\limits_{i=1}^{n\over d}[gcd(i,{n\over d})=1] \]

\[ans=\sum\limits_{d|n}n^{d-1}\varphi({n\over d}) \]

枚举约数即可。

#include <cstring>
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=100010;
typedef long long ll;
int n,P,T,m,ans;
int cnt[100],pri[100];
inline int phi(int x)
{
	int i,ret=1;
	for(i=2;i*i<=x;i++)	if(x%i==0)
	{
		ret=ret*(i-1),x/=i;
		while(x%i==0)	ret=ret*i,x/=i;
	}
	if(x>1)	ret=ret*(x-1);
	return ret;
}
inline int pw(int x,int y)
{
	x%=P;
	int z=1;
	while(y)
	{
		if(y&1)	z=z*x%P;
		x=x*x%P,y>>=1;
	}
	return z;
}
void dfs(int x,int d)
{
	if(x==m+1)
	{
		ans=(ans+phi(n/d)%P*pw(n,d-1))%P;
		return ;
	}
	for(int i=0;i<=cnt[x];i++,d*=pri[x])	dfs(x+1,d);
}
void work()
{
	scanf("%d%d",&n,&P),ans=m=0;
	int i,t=n;
	for(i=2;i*i<=t;i++)	if(t%i==0)
	{
		cnt[++m]=0,pri[m]=i;
		while(t%i==0)	t/=i,cnt[m]++;
	}
	if(t>1)	pri[++m]=t,cnt[m]=1;
	dfs(1,1);
	printf("%d\n",ans);
}
int main()
{
	scanf("%d",&T);
	while(T--)	work();
	return 0;
}//1 2 30000
posted @ 2018-01-07 08:42  CQzhangyu  阅读(193)  评论(0编辑  收藏  举报