1. 程式人生 > 其它 >Burnside引理與Polya定理

Burnside引理與Polya定理

簡述:

Burnside 引理:

一類狀態在一個置換群 (\(G\)) 的作用下本質不同的狀態(不同等價類)個數

\(∣X/G∣=\dfrac{1}{|G|}*\sum\limits_{r\in G}c(r)\)

\(c(r)\)表示在 \(r\) 這一置換作用下不動點(狀態不變的方案)的個數。

證明略。

Polya定理:

\(c(r)=k^m\)

\(k\) 為當前置換不同迴圈的個數,\(m\)為可賦予顏色數。

即位於同一迴圈(可互相替代,專業叫等價類)的元素顏色相同才能成為不動點。

栗子:

不同的翻轉操作即為置換群。

對於向下 $90^\circ $ 的操作,中間四個面,左邊的面,右邊的面即為三個不同的迴圈(等價類)。

例題:

一:P4980 【模板】Pólya 定理

題意:

給定一個 \(n\) 個點,\(n\) 條邊的環,有 \(n\) 種顏色,給每個頂點染色,問有多少種本質不同的染色方案。

做法:

咳咳,不是我懶得寫,主要 Latex 太難打了。

說明一下那個 \(gcd\) 那點:

對於一個旋轉 \(k\) 次的操作,當前點旋轉 \(n/gcd(n,k)\) 次就會回到當前點,即 \(k * ( n / gcd ( n,k ) ) %n == 0\) 。因此一個迴圈有 \(n/gcd(n,k)\) 節點,所以一共有 \(gcd(n,k)\) 個迴圈。

code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e5+8;
ll n;
const ll mod=1e9+7;
ll qm(ll x,int p)
{
	if(p==0) return 1;
	ll tmp=qm(x,p>>1);
	if(p&1) return tmp*tmp%mod*x%mod;
	return tmp*tmp%mod;
}
ll read()
{
	ll x=0;
	char c=getchar();
	while(c>'9'||c<'0') c=getchar();
	while(c>='0'&&c<='9'){
		x=x*10;
		x+=c-'0';
		c=getchar();
	}
	return x;
}
ll ol(ll x) 
{
  ll ans=x;
  for(int i=2;i*i<=x;i++)
    if(x%i==0){
      ans=ans*(i-1)%mod*qm(i,mod-2)%mod;
      while(x%i==0) x/=i;
    }
  if(x>1) ans=ans*(x-1)%mod*qm(x,mod-2)%mod;
  return ans%mod;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		n=read();
		ll ans=0;
		for(ll i=1;i*i<=n;i++)
		{
			if(n%i) continue;
			ans=(ans+ol(n/i)*qm(n,i-1)%mod)%mod;
			if(i*i!=n) ans=(ans+ol(i)*qm(n,n/i-1)%mod)%mod;
		}
		printf("%lld\n",ans);
	}
}

還有道一樣的題:【例題1】彩色項鍊1

改成 \(m\) 種顏色還是一樣的,只是改一下對應底數就行:
【例題2】彩色項鍊2

還有一道:P5233 [JSOI2012]愛之項鍊

本題還不清楚為啥 T 了。

關於波利亞定理的運用是一樣的,只是多了個 \(dp\) 和數學求斐波那契數列一類的通項公式的方法,後者還沒學會,不過可以用矩陣代替。

二:P4727 [HNOI2009]圖的同構計數

題意:

邊有兩種顏色,
求含 N 個點的圖在同構意義下不同構的圖的數目。

A圖與B圖被認為是同構的是指:A圖的頂點經過一定的重新標號以後,A圖的頂點集和邊集要完全與B圖一一對應。

做法:

對於自己來說可能更清楚的講解:

一個圖的置換,其實就是找一種排列來替換 \(1\thicksim n\)的點。

對於一個置換,將其拆為若干個互不影響的置換。

2 3 1

1 2 3

以上置換代表用 2 3 1 頂替 1 2 3。

例如:

5 3 2 1 4

1 2 3 4 5

將其拆為

5 1 4

1 4 5

3 2

2 3

下面將邊分為兩類。

一:兩端點在同一置換裡。

對於每個置換而言,將其按順序排好

例如
3 4 2 1

1 2 3 4

按 1 4 2 3 的順序排(因為元素是按這個順序流動的)。

排成一個正多邊形,會發現不同長度(即相隔不同個數的點)的邊屬於不同的等價類,因此有 \(\left[\dfrac{p[i]}{2}\right]\) 種不同的等價類,\(p[i]\) 為該置換點數。

二:兩端點在不同置換裡。

一條邊想轉回自己的位置要走 \(lcm(p[i],p[j])\) 下,即兩點同時回到原位,因此這 \(lcm(p[i],p[j])\) 條邊屬於一個等價類,共有 \(p[i]*p[j]\) 條邊,因此有 \(p[i]*p[j]/lcm(p[i],p[j])=gcd(p[i],p[j]\))。

綜上答案為:

\(b\)\(p\)\(K\)為拆分出來的置換個數。

最後,為了使計算快捷,直接列舉(爆搜)本質不同的置換,即將 \(n\) 拆分。

考慮有多少種拆成 \(P\) 這種方案的。

\(\dfrac{n!}{\prod_{p[i]!}}\) 算出點的分配方案。

對於一個置換裡的點,相當於一個 \(n\) 邊型頂點的排列,即圓排列,因此有對於一個置換來說有 \((p[i]-1)!\) 種排列方案,總的就要乘上 \(\prod_{(p[i]-1)!}\)

最後,對於長度相等的迴圈它們之間可以彼此交換,本質上是一樣的,因此還是會算重。設 \(c\) 表示表某個長度的迴圈的個數,則會算重 \(c!\) 倍。因此答案還需除以 \(\prod_{c[i]!}\)

算上除以的置換群大小 \(n!\),總答案為:

黑題完成!

code:

#include<bits/stdc++.h>
using namespace std;
const int N=65;
const int mod=997;
int n;
int p[N],tot;
int t[N];
int jc[N];
int ans=0;
int minn(int a,int b)
{
	return a>b?b:a;
}
int maxx(int a,int b)
{
	return a<b?b:a;
}
int qm(int x,int p)
{
	if(p==0) return 1;
	int tmp=qm(x,p>>1);
	if(p&1) return tmp*tmp*x%mod;
	return tmp*tmp%mod;
}
int gcd(int a,int b)
{
	if(b==0) return a;
	return gcd(b,a%b);
}
void work()
{
	int k=0;
	int tmp=1;
	memset(t,0,sizeof(t));
	for(int i=1;i<=tot;i++)
	{
		k+=p[i]/2;
		t[p[i]]++;
		tmp=tmp*qm(p[i],mod-2)%mod;
		for(int j=1;j<i;j++)
		k+=gcd(p[i],p[j]);
	}
	for(int i=1;i<=n;i++)
	{
		if(t[i]>1)
		tmp=tmp*qm(jc[t[i]],mod-2)%mod;
	}
	ans=(ans+qm(2,k)*tmp%mod)%mod;
}
void dfs(int x,int h,int s)
{
    if(!h) tot=x-1,work();
    if(h<s) return;
    for(int i=s;i<=h;i++)
	{
        p[x]=i;
        dfs(x+1,h-i,i);
    }
}
int main()
{
	cin>>n;
	jc[0]=1;
	for(int i=1;i<=n;i++)
	jc[i]=jc[i-1]*i%mod;
	dfs(1,n,1);
	cout<<ans<<endl;
}

如果有 \(m\) 種顏色也是一樣的:
P4128 [SHOI2006] 有色圖

卡常所得經驗:

\(1\).深搜別閒著沒事寫迭代加深,慢死了,這兩題的深搜可以多看一看。

\(2\).逆元可以預處理一下快一點。

黑題二倍經驗,好耶!