由一个集合 G G G和一个运算 ⋅ · ⋅ 构成,运算过程即为集合中任何两个元素 a , b a,b a,b, a ⋅ b a·b a⋅b的过程,运算 ⋅ · ⋅为具体给出的任意一种可行的运算,需要满足群公理的四个要求:
下面是一个置换,
( 1 2 3 . . . n a 1 a 2 a 3 . . . a n ) \begin {pmatrix} 1 & 2 & 3 & ... & n \\ a_1 & a_2 & a_3 & ... & a_n \end {pmatrix} (1a12a23a3......nan)
将群的运算(即基本变换方式)用 f : a i ( 1 ≤ i ≤ n ) ( n 为 G 中 元 素 个 数 , a i 互 不 相 同 , a i ∈ { 1 , 2 , . . . , n } ) f:a_i(1\leq i\leq n)(n为G中元素个数,a_i互不相同,a_i\in \{ 1,2,...,n\}) f:ai(1≤i≤n)(n为G中元素个数,ai互不相同,ai∈{1,2,...,n})表示。具体置换为把初状态 ( 1 , 2 , 3 , . . . , n ) (1,2,3,...,n) (1,2,3,...,n)的任意排列,用 a i a_i ai替代 i i i(设序列第 x x x个位置上的元素为 i i i,替代后序列上第 x x x个位置上的元素为 a i a_i ai)而得到新的一种排列,以上变换即为置换。
同样也可以看做置换 f : a i f:a_i f:ai分解成若干个不相关的循环组,每个循环组内的元素构成一个环,每个环上的元素都可以移动到下一个环上的位置而回到初始状态( i − > a i , a i − > a a i , . . . , − > i i->a_i,a_i->a_{a_i},...,->i i−>ai,ai−>aai,...,−>i),也就是集合的一一映射关系。群则将置换的过程中元素的位置变换转化成了运算,用数学符号表示出来。
将置换看做群的运算方式,一个封闭群即为一个置换群。
设存在一种群内元素的排列方式 q i q_i qi,使得经过置换 f : a i f:a_i f:ai后的排列与原来相同( i = a i = a a i = . . . = i i=a_i=a_{a_i}=...=i i=ai=aai=...=i)。则 q i q_i qi这种排列方式的状态是 f : a i f:a_i f:ai置换的不动点。
等价类即是定义一种等价关系,一个状态若可以通过特定的置换得到另一个状态,则这两个状态是等价的,同属一个等价类。
一些题目中“求本质不同的方案”,就是求等价类个数。
设置换 g : a i g:a_i g:ai的在可行的染色方案中的不动点个数为 C ( g ) C(g) C(g), G G G为 ∣ G ∣ |G| ∣G∣个不同的置换群的集合,则等价类个数为:
1 ∣ G ∣ ∑ i = 1 ∣ G ∣ C ( g i ) \frac 1 {|G|}\sum _{i=1} ^{|G|}C(g_i) ∣G∣1i=1∑∣G∣C(gi)
举个例子:
假设给正方形棋盘染黑白两色,轴对称同构,求本质不同方案数。
那么需要考虑:
这四种置换,也就是四个置换群,分别求不动点个数即可。
B u r n s i d e Burnside Burnside引理需要枚举所有置换的情况来求不动点,复杂度太高,于是引入 P o l y a Polya Polya定理来更简捷地求置换群的不动点个数。
之前提到了可以将置换 f : a i f:a_i f:ai分解成若干个循环组,考虑对于不动点来说,每个循环组中的元素的状态(颜色)必须完全一致,而不同循环组之间互不相关。因此对于置换 f : a i f:a_i f:ai,有 x x x种可选的颜色,循环组个数为 k k k, C ( f ) = x k C(f)=x^{k} C(f)=xk,更新 B u r n s i d e Burnside Burnside引理的式子,得到等价类个数为:
1 ∣ G ∣ ∑ i = 1 ∣ G ∣ x i k i \frac 1{|G|}\sum_{i=1} ^{|G|}x_i^{k_i} ∣G∣1i=1∑∣G∣xiki
还是上面的例子,这时只需要观察求出不动点个数即可,一般来说题目中所求的不动点都有一些特殊的规律和性质,以保证快速求出。
用 N N N种颜色染一条长度为 N N N的项链,旋转同构,求本质不同方案数。
观察规律发现对于旋转 1 , . . . , n 1,...,n 1,...,n次的不同置换而言,循环组个数即为 g c d ( n , i ) gcd(n,i) gcd(n,i)。
再套用 B u r n s i d e Burnside Burnside引理, P o l y a Polya Polya定理,等价类个数为:
∑ 1 n n g c d ( n , i ) \sum_1^nn^{gcd(n,i)} 1∑nngcd(n,i)
= ∑ d ∣ n n d ∑ i = 1 n d [ g c d ( i , n d ) = 1 ] =\sum_{d|n} n^d \sum_{i=1} ^{\frac n d}[gcd(i,\frac n d)=1] =d∣n∑ndi=1∑dn[gcd(i,dn)=1]
= ∑ d ∣ n n d ϕ n d =\sum _{d|n} n^d \phi \frac n d =d∣n∑ndϕdn
这样就可以 O ( n × log n ) O(\sqrt n\times \log n) O(n×logn)计算了,没有考虑求 ϕ d \phi d ϕd的复杂度,似乎远小于 d \sqrt d d?
代码
#include
#include
#include
using namespace std;
const int N=4e4,MX=1e9;
typedef long long ll;
int T,n,mod,ans,p[N],tot;
bool pri[N];
map<int,int>phi;
inline int mul(int x,int y){return x*y%mod;}
inline int ad(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline void pre()
{
int i,j;ll res;
for(i=2;i<=MX/i;++i){
if(!pri[i]) p[++tot]=i;
for(j=1;j<=tot;++j){
res=i*p[j];if(res*res>MX) break;
pri[res]=true;
if(i%p[j]==0) break;
}
}
}
inline int fp(int x,int y)
{
int re=1;x%=mod;
for(;y;y>>=1,x=mul(x,x))
if(y&1) re=mul(re,x);
return re;
}
inline int PHI(int x)
{
if(x==1) return 1;
int i,re=x;
for(i=1;i<=tot && p[i]*p[i]<=x;++i)
if(x%p[i]==0){
for(;x%p[i]==0;x/=p[i]);
re=re/p[i]*(p[i]-1);
}
if(x!=1) re=re/x*(x-1);
return re%mod;
}
int main(){
int i;
pre();
scanf("%d",&T);
for(;T;--T){
ans=0;
scanf("%d%d",&n,&mod);
for(i=1;i<=n/i;++i) if(n%i==0){
ans=ad(ans,mul(fp(n,i-1),PHI(n/i)));
if(i*i!=n)
ans=ad(ans,mul(fp(n,n/i-1),PHI(i)));
}
printf("%d\n",ans);
}
}
用 m a p map map会 W A WA WA, p o j poj poj交 # i n c l u d e < b i t s / s t d c + + . h > \# include<bits/stdc++.h> #include<bits/stdc++.h>会 C E CE CE,每次做 p o j poj poj都会先 C E CE CE多次…
题解
算是一个非常巧妙的运用了。
以上写的都非常清晰易懂,总之比我写的好多啦。