P4980 【模板】Polya定理
题目描述
给定一个(n)个点,(n)条边的环,有(n)种颜色,给每个顶点染色,问有多少种本质不同的染色方案,答案对(10^9+7)取模
注意本题的本质不同,定义为:只需要不能通过旋转与别的染色方案相同。
输入输出格式
输入格式:
第一行输入一个(t),表示有(t)组数据
第二行开始,一共(t)行,每行一个整数(n),意思如题所示。
输出格式:
共(t)行,每行一个数字,表示染色方案数对(10^9+7)取模后的结果
说明
(n leq 10^9,t leq 10^3)
注意置换只有(n)个,表示旋转的度数,没有翻转。
那么一个旋转(i)个点的置换的循环个数应该为(gcd(i,m))
带到(Polya)定理里面去,方案数为
[frac{1}{n}sum_{i=1}^n n^{gcd(i,n)}
]
[=frac{1}{n}sum_{i=1}^n n^ksum_{k=1}^n[gcd(i,n)=k]
]
[=frac{1}{n}sum_{kmid n} n^ksum_{k=1}^n[gcd(i,n)=k]
]
[=sum_{kmid n} n^{k-1}sum_{k=1}^{frac{n}{k}}[gcd(i,n)=1]
]
[=sum_{k|n}n^{k-1}varphi(frac{n}{k})
]
然后直接暴力搞,复杂度是常数很小的(O(Tn^{frac{3}{4}}))
Code:
#include <cstdio>
const int mod=1e9+7;
int Euler(int n)
{
int phi=n;
for(int i=2;i*i<=n;i++)
if(n%i==0)
{
phi=phi-phi/i;
while(n%i==0) n/=i;
}
if(n!=1) phi=phi-phi/n;
return phi;
}
#define mul(a,b) (1ll*(a)*(b)%mod)
#define add(a,b) ((a+b)%mod)
int qp(int d,int k){int f=1;while(k){if(k&1)f=mul(f,d);d=mul(d,d),k>>=1;}return f;}
int Polya(int n)
{
int ans=0;
for(int i=1;i*i<=n;i++)
{
if(n%i) continue;
ans=add(ans,mul(qp(n,i-1),Euler(n/i)));
if(i*i!=n)
ans=add(ans,mul(qp(n,n/i-1),Euler(i)));
}
return ans;
}
int main()
{
int n,T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
printf("%d
",Polya(n));
}
return 0;
}
2018.12.21