洛谷4980【模板】Polya定理题解(置换群+数论)

题目:luogu4980.
题目大意:给定一个长度为 n n n的环,用 n n n种颜色染色,求本质不同(旋转后相同算一种)的染色方案数.
数据组数 T ≤ 1 0 3 T\leq 10^3 T103 1 ≤ n ≤ 1 0 9 1\leq n\leq 10^9 1n109,答案对 1 0 9 + 7 10^9+7 109+7取模.

具体Polya定理相关内容参考群论与置换群入门.

考虑将旋转放入一个置换群,那么置换群中的元素有循环 0 0 0位,循环 1 1 1位,循环 2 2 2位…循环 n − 1 n-1 n1位,其中循环 0 0 0为可以看成循环 n n n位.

然后使用Ploya定理,设 C ( A ) C(A) C(A)表示 A A A可以拆分的轮换数,那么有:
a n s = 1 n ∑ i = 1 n n C ( A i ) ans=\frac{1}{n}\sum_{i=1}^{n}n^{C(A_i)} ans=n1i=1nnC(Ai)

现在的问题是如何计算每一个置换可以拆成几个轮换.

考虑循环 a a a位如何计算可以拆解的轮换数.容易发现拆解出来的每个轮换大小都是相等的,可以用 n n n除掉轮换大小,问题变为求轮换大小.

显然轮换大小应该是以下方程中 x x x的最小正整数解:
a x ≡ 0    ( m o d    n ) ax\equiv 0\,\,(mod\,\,n) ax0(modn)

可以转化为以下方程中 x x x的最小正整数解:
a x = n y ax=ny ax=ny

容易发现 x x x为最小正整数解时, a x = n y = l c m ( n , a ) ax=ny=\mathrm{lcm}(n,a) ax=ny=lcm(n,a).

于是轮换数就是:
n l c m ( n , a ) a = n a l c m ( n , a ) = gcd ⁡ ( n , a ) \frac{n}{\frac{\mathrm{lcm}(n,a)}{a}}=\frac{na}{\mathrm{lcm}(n,a)}=\gcd(n,a) alcm(n,a)n=lcm(n,a)na=gcd(n,a)

也就是说对于一个循环位移 a a a位的置换 A A A C ( A ) = gcd ⁡ ( n , a ) C(A)=\gcd(n,a) C(A)=gcd(n,a).

那么整道题目的答案为:
1 n ∑ i = 1 n n gcd ⁡ ( n , i ) \frac{1}{n}\sum_{i=1}^{n}n^{\gcd(n,i)} n1i=1nngcd(n,i)

可是 n n n 1 0 9 10^9 109级别的数据,并不能暴力枚举…

所以开始推式子:
1 n ∑ i = 1 n n gcd ⁡ ( n , i ) = 1 n ∑ d ∣ n n d ∑ i = 1 n d [ gcd ⁡ ( n d , i ) = 1 ] = 1 n ∑ d ∣ n n d ϕ ( n d ) = ∑ d ∣ n n d − 1 ϕ ( n d ) \frac{1}{n}\sum_{i=1}^{n}n^{\gcd(n,i)}\\ =\frac{1}{n}\sum_{d|n}n^{d}\sum_{i=1}^{\frac{n}{d}}[\gcd(\frac{n}{d},i)=1]\\ =\frac{1}{n}\sum_{d|n}n^{d}\phi(\frac{n}{d})\\ =\sum_{d|n}n^{d-1}\phi(\frac{n}{d}) n1i=1nngcd(n,i)=n1dnndi=1dn[gcd(dn,i)=1]=n1dnndϕ(dn)=dnnd1ϕ(dn)

这个东西计算起来时间复杂度是 O ( τ ( n ) n ) O(\tau(n)\sqrt{n}) O(τ(n)n ),不能过.

可以先把 n n n的质因数分解出来,然后每次求它的某个因数的 ϕ \phi ϕ值时,可以直接暴力枚举 n n n的质因数去算,时间复杂度 O ( T ( τ ( n ) log ⁡ n ) ) O(T(\tau(n)\log n)) O(T(τ(n)logn)).

代码如下:

#include
using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=50,mod=1000000007;

int add(int a,int b,int p=mod){return a+b>=p?a+b-p:a+b;}
int sub(int a,int b,int p=mod){return a-b<0?a-b+p:a-b;}
int mul(int a,int b,int p=mod){return (LL)a*b%p;}
void sadd(int &a,int b,int p=mod){a=add(a,b,p);}
void ssub(int &a,int b,int p=mod){a=sub(a,b,p);}
void smul(int &a,int b,int p=mod){a=mul(a,b,p);}
int Power(int a,int k,int p=mod){int res=1;for (;k;k>>=1,smul(a,a,p)) if (k&1) smul(res,a,p);return res;}

int n,d[N+9],cd;

void Get_d(int n){
  cd=0;
  int now=n;
  for (int i=2;i*i<=n;++i)
    if (now%i==0)
      for (d[++cd]=i;now%i==0;now/=i);
  if (now>1) d[++cd]=now;
}

int Get_phi(int n){
  int res=n;
  for (int i=1;i<=cd;++i)
    if (n%d[i]==0) res=res/d[i]*(d[i]-1);
  return res;
}

int ans;

Abigail into(){
  scanf("%d",&n);
}

Abigail work(){
  ans=0;
  Get_d(n);
  for (int i=1;i*i<=n;++i)
    if (n%i==0){
  	  sadd(ans,mul(Power(n,i-1),Get_phi(n/i)));
  	  if (i*i^n) sadd(ans,mul(Power(n,n/i-1),Get_phi(i)));
    }
}

Abigail outo(){
  printf("%d\n",ans);
}

int main(){
  int T;
  scanf("%d",&T);
  while (T--){
    into();
    work();
    outo();
  }
  return 0;
}

你可能感兴趣的:(洛谷4980【模板】Polya定理题解(置换群+数论))