机器人的项链 HDU - 2239
刚开始做欧拉函数的题目,先说一下欧拉函数吧。
euler(x)就是计算1~n中有多少个与n互质的个数;
公式:euler(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…(1-1/pn);
具体实现两种:
//1.
int euler(int n)
{ //返回euler(n)
int res=n,a=n;
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
{
res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出
while(a%i==0) a/=i;
}
}
if(a>1) res=res/a*(a-1);
return res;
}
//2,打表
//筛选法打欧拉函数表
#define Max 1000001
int euler[Max];
void Init()
{
euler[1]=1;
for(int i=2;i<Max;i++)
euler[i]=i;
for(int i=2;i<Max;i++)
if(euler[i]==i)
for(int j=i;j<Max;j+=i)
euler[j]=euler[j]/i*(i-1);//先进行除法是为了防止中间数据的溢出
}/*对于这个题,需要对欧拉函数进行一个简单 的“优化”。
否者会超时。
即先筛素数再用模板。
euler (x): 1~x中互质的数的个数,
*** euler(n/i): 1~n中有多少个数的最大公约数是i;
即:
int prime[maxn];
bool isprime[maxn];
int cnt;
void Init()
{
cnt=0;
memset(isprime,0,sizeof isprime);
ll i,j;
for(i=2;i<=N;i++)
{
if(!isprime[i])
prime[cnt++]=i;
for(j=0;j<cnt && i*prime[j]<=N;j++)
{
isprime[i*prime[j]]=true;
}
}
cnt--;
isprime[1]=true;
}
ll euler(ll n)
{
ll i;
ll tempn=n;
ll ans=n;
for(i=0;i<=cnt && prime[i]*prime[i]<=n;i++)
{
if(n%prime[i]==0)
{
ans=ans/prime[i]*(prime[i]-1);
while(tempn%prime[i]==0)
tempn/=prime[i];
}
}
if(tempn>1)
ans=ans/tempn*(tempn-1);
return ans;
}
再说一下Polya定理:
设
是n个对象的一个置换群, 用m种颜色染图这n个对象,则不同的染色方案数为:
其中
,为的循环节数
简单来讲就是用m中颜色去染n个对象,这n个对象是环状的,求方案数。
置换:设X为一个有限集,π是X到X的一个--变换,那么称π是X上的一个置换。
比如:
{ 1 2 3 4 5 }的置换群{ (1 2 3 4 5 ) ( 2 3 4 5 1) (3 4 5 1 2) (4 5 1 2 3) (5 1 2 3 4) }
循环节:
1 2 3 4 5
3 4 1 2 5 循环节就是比较上下两个数列:如果出现上面的与下面的集合能够出现全等。
比如:上面的1->3,3->1,这就是一个循环节,同样{ 2->4,4->4},{ 5->5 },一共三个循环节。
循环节的求法:
把所有置换写出来,就能知道它们的循环节数了;
对于旋转,有c(ai) = gcd(n,i),i为转动幅度,1~n,gcd是求最大公约数。
对于 这个题:ans=sum(m^gcd(n,i))/n;
又因为n很大,所以要用欧拉函数处理,即用欧拉函数枚举n的约数 ,找到最大公约数为 i 的个数,ans=Sum(euler(n/i)*m^i)/n;
除n本来要用逆元,但是有些数的逆元不存在,所以不能用,
正确做法是枚举每个数找到ans最小值。
比如:
{ 1 2 3 4 5 }的置换群{ (1 2 3 4 5 ) ( 2 3 4 5 1) (3 4 5 1 2) (4 5 1 2 3) (5 1 2 3 4) }
循环节:
1 2 3 4 5
3 4 1 2 5 循环节就是比较上下两个数列:如果出现上面的与下面的集合能够出现全等。
比如:上面的1->3,3->1,这就是一个循环节,同样{ 2->4,4->4},{ 5->5 },一共三个循环节。
循环节的求法:
把所有置换写出来,就能知道它们的循环节数了;
对于旋转,有c(ai) = gcd(n,i),i为转动幅度,1~n,gcd是求最大公约数。
对于 这个题:ans=sum(m^gcd(n,i))/n;
又因为n很大,所以要用欧拉函数处理,即用欧拉函数枚举n的约数 ,找到最大公约数为 i 的个数,ans=Sum(euler(n/i)*m^i)/n;
除n本来要用逆元,但是有些数的逆元不存在,所以不能用,
正确做法是枚举每个数找到ans最小值。
代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<stdlib.h>
#include<math.h>
#include<vector>
#include<list>
#include<map>
#include<stack>
#include<queue>
#include<algorithm>
#include<numeric>
#include<functional>
#define maxn 80010
using namespace std;
const int MOD=9937;
typedef long long ll;
bool vis[maxn];
int prime[maxn];
int len;
void Init()
{
len=0;
memset(vis,0,sizeof vis);
for(int i=2;i<=maxn;i++)
{
if(!vis[i])
{
prime[len++]=i;
for(int j=i*2;j<=maxn;j+=i)
vis[j]=1;
}
}
}
int euler(int n)
{
int res=n,a=n;
for(int i=0;(ll)prime[i]*prime[i]<=a&&i<len;i++)
{
if(a%prime[i]==0)
{
res=res/prime[i]*(prime[i]-1);
while(a%prime[i]==0) a/=prime[i];
}
}
if(a>1) res=res/a*(a-1);
return res%MOD;
}
int Pow(int a,int b)
{
a%=MOD;
int res=1;
while(b)
{
if(b&1)
res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
int main()
{
int n,m;
Init();
while(scanf("%d%d",&n,&m)!=EOF)
{
ll ans=0;
for(int i=1;i<=sqrt(n)+1;i++)
{
if(n%i==0)
{
ans=(ans+euler(n/i)*Pow(m,i)%MOD)%MOD;
if(i!=n/i)
ans=(ans+euler(i)*Pow(m,n/i)%MOD)%MOD;// 注意n/i 的计算
}
}
int tot = ans;
for (ans = 0; ans < MOD; ans++)
if ((ll)ans*n % MOD == tot % MOD)
break;
printf ("%d\n", ans);
}
return 0;
}