测试地址:Color
题目大意:有
N
种颜色的珠子任意多个,要用
做法:前几天POJ炸了,所以拖到今天才写这一篇……
这一题需要用到Polya定理(真名不是这个o,但是那个字符不会打QAQ)+欧拉函数。
首先看Polya定理的内容:对于一个置换群
其中 ci 为第 i 种置换的循环数,如置换
那么再看这一题,容易看出 |G|=m=N ,那么关键就是求 ci 了。我们不妨将所有珠子顺时针依次编号为 1 到
设 di=gcd(N,i) ,可以看出 di 一定是 N 的因子,那么我们可不可以枚举
1N∑L|Nφ(NL)NL
考虑到模意义下乘以一个分数比较困难,所以我们直接把 1/N 乘进去得到: ∑L|Nφ(N/L)NL−1 ,这样就不用乘分数了。按照上面的方法优化,时间复杂度就降了很多了,只要在枚举 L 的时候实时算出
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
ll T,N,P,tot=0,fac[25],num[25],ans;
void find_factor()
{
ll s=N;
tot=0;
for(ll i=2;i*i<=s;i++)
if (!(s%i))
{
fac[++tot]=i;
num[tot]=0;
while(!(s%i)) s/=i,num[tot]++;
}
if (s>1) fac[++tot]=s,num[tot]=1;
}
ll phi(ll x)
{
ll s=x,ss=x;
for(ll i=2;i*i<=s;i++)
if (!(s%i))
{
ss=ss/i*(i-1);
while(!(s%i)) s/=i;
}
if (s>1) ss=ss/s*(s-1);
return ss;
}
ll power(ll a,ll b)
{
ll s=a,sum=1;
while(b)
{
if (b&1) sum=(sum*s)%P;
b>>=1;s=(s*s)%P;
}
return sum;
}
void dfs(int i,ll d)
{
if (i>tot)
{
ans=(ans+phi(N/d)*power(N,d-1))%P;
return;
}
for(int j=0;j<=num[i];j++)
{
dfs(i+1,d);
d*=fac[i];
}
}
int main()
{
scanf("%lld",&T);
while(T--)
{
scanf("%lld%lld",&N,&P);
find_factor();
ans=0;
dfs(1,1);
printf("%lld\n",ans);
}
return 0;
}