【BZOJ1488】[HNOI2009]圖的同構計數

題目鏈接

題意

求 n 個點的同構意義下不同的圖的數量。(n60)(n\leq 60)

Sol

PolyaPolya 定理的練手題。
我們這裏先把邊的存在與否變成對邊進行黑白染色,白色代表不存在,這樣就變成了一個對完全圖中的邊進行染色的問題,於是可以使用 對BurnsideBurnside引理 進行優化後的 PolyaPolya 定理。

顯然總的置換羣大小是 n!n! 對應了每一種對邊進行重新編號的方案,關鍵就在於要求出不動點的個數。
使用 PolyaPolya 定理,那麼只需要求出所有關於邊的置換中每一個置換被分成的循環總個數(也就是等價類總個數)。
顯然每一個對於點的置換都唯一對應一個對於邊的置換。但是即使是這樣,置換數目依然是不可枚舉的。所以我們肯定要另求他法。
由於最後的計算只和被分解成的循環個數有關,於是我們可以爆搜出所有的整數拆分方案代表最後循環的格式。
考慮可能處於一個等價類的邊集要麼這些邊的端點都位於一個點的循環內部,要麼這些邊的端點僅連接了兩個不同的循環。
首先考慮一個循環內部邊集的等價類個數,假設有 n 個點,那麼只用考慮這 n 個點的完全圖中的邊。我們把這些點排成一排,這樣每一條邊就有了一個跨度,循環一次相當於把這些點全部往前挪動一位並且把最前面的點扔到最後面去,這樣顯然跨度相同的邊會在一個等價類裏,然後由於這是一個環 那麼跨度爲 dd 的和跨度爲 nd+1n-d+1 的也是等價的 , 跨度總共有(n1)(n-1)種於是總共有 n2\lfloor \frac{n}{2} \rfloor 種等價類。
然後考慮兩個循環之間的等價類個數 , 假設大小爲別爲 a,ba,b , 我們把兩個循環就看作是一個環 , 每次置換相當於是把兩個環同時轉動一圈 , 這樣子總共會便歷到的邊就有 lcm(a,b)lcm(a,b) 條 , 然後邊總共有 aba*b 條,於是就有 ablcm(a,b)=gcd(a,b)\frac{a*b}{lcm(a,b)}=gcd(a,b) 種等價類。

這樣我們就算完一種類型的置換的貢獻了,顯然這樣的置換是有許多個的,我們還要乘上一個係數,也就是滿足我們整數拆分中枚舉的格式的置換個數 , 考慮一個排列然後把重複的去掉即可,總數爲:
n!i=1ci!ici\dfrac{n!}{\prod_{i=1}c_i!*i^{c_i}}
cic_i表示大小爲 ii 的循環個數
我們把一個排列直接認爲是把循環按大小順序排好的結果。
這樣對於相同大小的循環,他們隨便排列都是沒有關係的,所以除掉 ci!c_i! , 然後對於大小爲 i 的一個循環來說 , 它的內部不斷循環也是相同的方案 , 所以每一個大小爲 ii 的循環都要在方案中再除掉 ii

code:

#include<bits/stdc++.h>
#define Set(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=61;
const int mod=997;
template <typename T> inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}
typedef long long ll;
typedef unsigned long long ull;
template <typename T>inline void Inc(T&x,int y){x+=y;if(x>=mod) x-=mod;return;}
template <typename T>inline void Dec(T&x,int y){x-=y;if(x <  0) x+=mod;return;}
template <typename T>inline int fpow(int x,T k){int ret=1;for(;k;k>>=1,x=(ll)x*x%mod) if(k&1) ret=(ll)ret*x%mod;return ret;}
int Sum(int x,int y){x+=y;if(x>=mod) return x-mod;return x;}
int Dif(int x,int y){x-=y;if(x < 0 ) return x+mod;return x;}
int n;
int size[N],num[N],top=0,rest=0,cnt=0,mx=0;
int Gcd[N][N],bits[N],finv[N];
int ans=0;
inline int gcd(int a,int b){return b? gcd(b,a%b):a;}
inline void Calc(){
	int nomove=0;int Inv=1;
	for(int i=1;i<=top;++i) {
		nomove+=num[i]*(size[i]>>1);
		Inv=Inv*finv[num[i]]*fpow(fpow(size[i],num[i]),mod-2)%mod;
		nomove+=num[i]*(num[i]-1)/2*size[i];
		for(int j=i+1;j<=top;++j) {
			int g=Gcd[size[i]][size[j]];
			nomove+=g*num[i]*num[j];
		}
	}
	Inc(ans,fpow(2,nomove)*Inv%mod);
	return;
}
void Dfs(int pre){
	int now=rest;if(!rest) {Calc();return;}
	for(int i=pre+1;i<=now;++i) {
		for(int j=1;j*i<=now;++j) {
			rest=now-i*j;
			size[++top]=i,num[top]=j;
			Dfs(i);--top;
		}
	}
}
int main()
{
	init(n);if(!n) return puts("1"),0;bits[0]=1,finv[1]=1,bits[1]=2;
	for(int i=2;i<=n;++i) bits[i]=Sum(bits[i-1],bits[i-1]),finv[i]=(ll)(mod-mod/i)*finv[mod%i]%mod;
	for(int i=2;i<=n;++i) finv[i]=(ll)finv[i-1]*finv[i]%mod;
	for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) Gcd[i][j]=gcd(i,j);
	rest=n;Dfs(0);cout<<ans<<endl;
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章