Burnside引理與polya定理


1、置換

  置換簡單來說就是對元素進行重排列,如下圖所示。置換是[1,n]到[1,n]的一一映射。

  舉個直觀的例子,將正方形繞其中心逆時針旋轉90度,可以看成是正方形四個頂點的一個置換。關於置換、置換群的具體理論,請參考其他資料,此處有個大致印象就好。下面描述幾個結論。 

  (1)置換可以分解成若干循環,方法為:連邊1->a1,2->a2,…,i->ai,…,n->an,任取一個元素,順着有向邊走,直到回到出發點,即形成一個環,剩余元素如法炮制。

  (2)如果一個狀態經過置換 f 后跟原來相同,即S[1] = S[a1], S[2] = S[a2], …, S[n] = S[an]。則稱該狀態為 f 的不動點。

  (3)題目中常常出現“本質不同的方案數”,一般是指等價類的數目,題目定義一個等價關系,滿足等價關系的元素屬於同一等價類。等價關系通常是一個置換集合F,如果一個置換能把其中一個方案映射到另一個方案,則二者是等價的。

2、burnside引理

  對於一個置換f,若一個染色方案s經過置換后不變,稱s為f的不動點。將f的不動點數目記為C(f),則可以證明等價類數目為所有C(f)的平均值。

 

  如上圖(圖片來自百度百科“burnside引理”)所示,對於四個置換{逆時針旋轉0°,逆時針旋轉90°,逆時針旋轉180°,逆時針旋轉270°},其不動點數分別為16, 2, 4, 2。所以等價類數目為(16+2+4+2)/4 = 6。

3、polya定理

   polay定理實際上是burnside引理的具體化,提供了計算不動點的具體方法。

  假設一個置換有k個循環,易知每個循環對應的所有位置顏色需一致,而任意兩個循環之間選什么顏色互不影響。因此,如果有m種可選顏色,則該置換對應的不動點個數為m^k。用其替換burnside引理中的C(f),得到等價類數目為:

  其中|F|表示置換的數目,ki表示第i個置換包含的循環個數。 

4、例題

// LA_3641 Leonardo's Notebook
#include <cstdio>
using namespace std;

int main()
{
    int T;
    char B[30];
    int c[30];
    bool v[30];
    scanf("%d", &T);
    while(T--)
    {
        scanf("%s", B);
        for(int i = 0; i <= 26; i++) c[i] = 0, v[i] = 0;
        for(int i = 0; i < 26; i++){
            if(v[i] == 0) {
                v[i] = 1;
                int t = 1, j = B[i]-'A';
                for(; j != i; j = B[j]-'A') t++, v[j] = 1;
                c[t]++;
            }
        }
        bool flag = true;
        for(int i = 2; i <= 26; i += 2) if(c[i]&1) flag = false;
        puts(flag ? "Yes" : "No");
    }
    return 0;
}

 

 

// LA_3510 Pixel Shuffle
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;

char oper[35][10];

const int maxn = 1024;

int ori[maxn*maxn];
#define ID(i, j) ((i)*n+(j))//注意這樣定義函數的時候,一定不要偷懶,省略任意一個括號,都可能產生致命的后果


int NewPos(int i, int j, int n, char *op)
{
    if(op[0] == 'i') return ID(i, j);
    if(op[0] == 'r') return ID(n-1-j, i);
    if(op[0] == 's') return ID(i, n-1-j);
    if(op[0] == 'b' && op[1] == 'h') return (2*i >= n) ? ID(i, n-1-j) : ID(i, j);
    if(op[0] == 'b' && op[1] == 'v') return (2*i >= n) ? ID(n/2+n-i-1, j) : ID(i, j);
    if(op[0] == 'd') return (i&1) ? ID(n/2+i/2, j) : ID(i/2, j);
    if(op[0] == 'm') {
        int k = (i>>1)<<1;
        if(j < n/2) return ID(k, (j<<1)+(k!=i));
        else {
            j -= n/2;
            return ID(k+1, (j<<1)+(k!=i));
        }
    }
}

void apply(int* image, int n, char *op)//維護一個當前的序列
{
    bool div = 0;
    if(op[strlen(op)-1] == '-') div = 1;
    for(int i = 0; i < n*n; i++) ori[i] = image[i];
    int mx = -1, mi = -1;
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++){
            int p = ID(i, j), p2 = NewPos(i, j, n, op);
            if(div) image[p] = ori[p2];
            else image[p2] = ori[p];
        }
    }
}

bool v[maxn*maxn];

int gcd(int x, int y) { return y == 0 ? x : gcd(y, x%y);}
int lcm(int x, int y) { return x/gcd(x, y)*y; }

int solve(int* image, int n)//對於給定的置換,計算其各個循環長度的最小公倍數
{
    int ans = 1;
    for(int i = 0; i < n; i++) v[i] = 0;
    for(int i = 0; i < n; i++){
        if(!v[i]){
            v[i] = 1;
            int c = 1, j = image[i];
            for( ; j != i; j = image[j]) c++, v[j] = 1;
            ans = lcm(ans, c);
        }
    }
    return ans;
}
int cur[maxn*maxn];
int main()
{
    int T, n;
    scanf("%d %d", &T, &n);
    while(T--)
    {
        int c = 0, n1;
        while(~scanf("%s", oper[c]))
        {
            if(isdigit(oper[c][0])){
                sscanf(oper[c], "%d", &n1);
                break;
            }
            c++;
        }
        for(int i = 0; i < n*n; i++) cur[i] = i;
        for(int i = c-1; i >= 0; i--)
            apply(cur, n, oper[i]);
        printf("%d\n", solve(cur, n*n));
        if(T) puts("");
        n = n1;
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM