前言
求·······的方案數
循環同構算一種
一臉懵逼
(於是我覺得系統的學一遍Burnside引理和Polya定理)
正文
置換
置換的概念
對於一個排列ai
我們想成i輸進去會出來一個ai
那麼我們如果輸入一個排列,將能得到一個排列
就比如我們輸入的排列是1到n有序的,那麼這個置換就是
(1a12a23a3⋅⋅⋅⋅⋅⋅nan)
當然我們如果隨便輸入一個排列bi,並且得到一個新的排列,那麼這個置換就是
(b1ab1b2ab2b3ab3⋅⋅⋅⋅⋅⋅bnabn)
只要排列ai相同,無論輸入什麼排列bi生成的置換都是一樣的
即置換與橫向位置無關,只與縱向的對應關係有關
我們設A={a1,a2⋅⋅⋅an},即∣A∣=n,我們稱上面的那些置換爲A上的n次置換
不難發現,本質不同的n次置換一共有n!種
置換的乘積形式
我們發現,對於一個置換,一個位置的數如果是i,進行若干次置換後,這個位置的數會變回i
比如一個置換
(122331465564)
一個數1其變化是1→2→3→1
那麼我們可以寫成(2,3,1)
所有的循環的乘積就是這個置換的不相交循環的乘積寫法
比如剛剛這個置換就是(2,3,1)(6,4)(5)
我們發現5這個位置無論進行幾次置換值都是5,對於這些位置我們稱其爲不動點
置換羣
是一種羣,裏面每個元素都是置換
然後滿足羣的一些性質
對於一個n,在n!種本質不同的n次置換中選x個(x≥1)置換作爲元素的羣被稱爲x階n次置換羣,特殊的,當x=n!時,其被稱爲n次對稱羣
對於一個n次置換羣,我們發現其元素中必有置換
(a1a1a2a2a3a3⋅⋅⋅⋅⋅⋅anan)
另外,若其元素中有置換
(b1ab1b2ab2b3ab3⋅⋅⋅⋅⋅⋅bnabn)
則其元素中也必有
(ab1b1ab2b2ab3b3⋅⋅⋅⋅⋅⋅abnbn)
以上就差不多是一些定義了
百度百科鏈接,羣
良好並且完整的置換羣課件,鏈接
Burnside引理
設G={a1,a2,…ag}(a是置換)是目標集[1,n]上的置換羣。每個置換都寫成不相交循環的乘積。c1(ak)是在置換ak的作用下不動點的個數,也就是長度爲1的循環的個數。通過上述置換的變換操作後可以相等的元素屬於同一個等價類。若G將[1,n]劃分成l個等價類,則:
l=∣G∣1[c1(a1)+c1(a2)+⋅⋅⋅+c1(a∣G∣)]
證明:
對於一個置換a,將其寫成不相交循環的乘積
我們設χa(x)={01x不是不動點x是不動點
根據定義,我們展開
ai∈G∑c1(ai)=ai∈G∑1≤j≤n∑χai(j)=1≤j≤n∑ai∈G∑χai(j)
我們設Z(x)爲滿足x是不動點的置換集合,我們發現上式的後半部分可以直接套入
1≤j≤n∑ai∈G∑χai(j)=1≤j≤n∑∣Z(j)∣
我們發現,對於同一個等價類裏的元素x1,x2,一定滿足Z(x1)=Z(x2),原因是Z(x)集合是一個置換羣元素集合的子集
這樣我們設C爲一個等價類元素集合,設c=∀x∈C,那麼無論c的取值,Z(c)的值都是一樣的
那麼繼續上式
1≤j≤n∑∣Z(j)∣=C∑∣C∣∗Z(c)=∣G∣C∑1=∣G∣⋅∣C∣=∣G∣l
對於第二個等號,可能你會感到疑惑
這裏用到了∣C∣∗Z(c)=∣G∣,即“軌道-穩定集定理”,這裏就不展開了,因爲這個的證明還需要拉格朗日定理等羣論基本知識,有興趣的可以看這篇博客,裏面有一些基本概念的介紹,另外,據說具體證明可以參照 《組合數學》(第5版)P181 定理4-11(暫未考證)
綜上l=∣G∣1ai∈G∑c1(ai)
這篇博客裏有比較清楚的例子,我這裏就不舉例了
我們發現,在染色問題循環同構的題中,∣G∣是旋轉方式數,∣A∣是所有染色種類數,算法的複雜度是O(∣G∣∣A∣)的,而後者很容易變得很大,這個時候就需要用到Polya定理
(也正因爲如此在OI中應用不多)
Polya定理
前提條件:如果將[1,n]用k種顏色進行染色
我們定義λ(ai)爲置換ai的循環數量
Polya定理:
l=∣G∣1ai∈G∑kλ(ai)
Polya定理其實就是一種Burnside引理的具體化,就是把上面的c1(ai)算出來了
例題
[poj1286]Necklace of Beads
題目大意:現在有3種顏色的珠子,問有多少種長度爲n的環,旋轉、軸對稱同構算一種
題解: 我們發現對於一個n的置換隻有2∗n種,直接套polya定理求環數量即可,複雜度O(n2)
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
typedef long long ll;
#define rg register
template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);}
ll n,ans[24];
int a[24];
bool vis[24];
ll pow(ll x,ll y)
{
ll res=1;
for(;y;y>>=1,x=x*x)if(y&1)res*=x;
return res;
}
void dfs(const int x)
{
if(vis[x])return;
vis[x]=1;
dfs(a[x]);
}
void calc()
{
memset(vis,0,sizeof(vis));
int k=0;
for(rg int i=0;i<n;i++)
if(!vis[i])
dfs(i),k++;
ans[n]+=pow(3,k);
}
int main()
{
for(n=1;n<24;n++)
{
for(rg int i=0;i<n;i++)
{
for(rg int j=0;j<n;j++)a[j]=(i+j)%n;
calc();
for(rg int j=0;j<n-j-1;j++)std::swap(a[j],a[n-j-1]);
calc();
}
ans[n]/=2*n;
}
while(1)
{
read(n);
if(n==-1)break;
print(ans[n]),putchar('\n');
}
return 0;
}