1. 程式人生 > >[HNOI2009] 圖的同構

[HNOI2009] 圖的同構

但是 sta www ons 列數 calc pre for out

1488: [HNOI2009]圖的同構

Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 700 Solved: 470
[Submit][Status][Discuss]

Description

求兩兩互不同構的含n個點的簡單圖有多少種。

簡單圖是關聯一對頂點的無向邊不多於一條的不含自環的圖。

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

Input

輸入一行一個整數N,表示圖的頂點數,0<=N<=60

Output

輸出一行一個整數表示含N個點的圖在同構意義下互不同構的圖的數目,答案對997取模。

Sample Input

輸入1
1

輸入2
2

輸入3
3

Sample Output

輸出1
1

輸出2
2

輸出3
4
很經典的一類等價圖計數問題。。。。 可以先等價於每條邊可以染兩種顏色,求兩兩本質不同的圖的個數。。。 最樸素的想法就是找出所有邊的置換,對每個置換求一下環數,然後用p????引理算一下方案數,最後除以總置換數(也就是邊數的階乘的逆元)就可以得到答案了。。。 但是有一些邊的置換是會出現矛盾的。。。因為邊置換要求兩個頂點映射到新邊的兩個頂點上,難免會產生沖突。 於是我們換一種思路,枚舉點的置換,邊的置換就隨之確定了,並且會不重不漏的算上所有邊的置換(想一想為什麽)
現在的問題就變成了:給你一個點的置換,問你邊的置換的環數。 先把點置換表示成若幹個環的乘積,然後同一個環內部點之間的邊的置換環數為 (點的)環大小/2,連接不同環之間的所有邊的置換環數為 gcd(siz1,siz2)。 理解這一步非常的關鍵。 首先不妨將(點的)環上亂序的數寫成{1,2,....,siz},這樣並不影響最後的(邊的)環數。 這樣 (1,i) 這條邊是和任意 ( j , (j+i-1)%siz +1)的邊等價的,而且當i>n/2的時候顯然不合法,因為(i,1)這條邊之前已經在別的等價類裏被算過了。 於是我們就求出了第一種邊的置換環數。。。
第二種的話,由於每轉一圈,一個環中的點對應的另一個環中的點會產生 |siz1 - siz2|的偏位移,由同余相關定理可以很輕松的算出每個環的大小都是 siz1 * siz2 / gcd(siz1 , siz2),所以環數就是 gcd(siz1 , siz2)了。 於是就可以A題了??? 並不,枚舉排列的時候就已經超時了2333 不過觀察上述做法可以發現,一種置換的方案數之和 (點的)環的大小(可重)集合有關。 所以我們改用??數(我也忘了叫啥了。。反正不大)的復雜度枚舉一下可重集中的元素是哪些,算出了這個可重集的方案數之後再乘上可以映射到這個可重集上的排列數 加到答案裏就好啦。。。。 至於映射數? (組合數學入門問題,自己推推吧2333)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int ha=997,mod=ha-1;

inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}

inline int ksm(int x,int y){
	int an=1;
	for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
	return an;
}

int gcd[73][73],n,ci[1005],ans,jc[73],ni[73];
int m,a[65],tot,now,C[73][73],b[65];

inline void calc(){	
    m=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=b[i];j++) a[++m]=i;

	tot=0,now=1;
	for(int i=1;i<=m;i++) tot+=a[i]>>1;
	for(int i=1;i<=m;i++)
	    for(int j=i+1;j<=m;j++) tot+=gcd[a[i]][a[j]];
	
	if(tot>=mod) tot%=mod;
	
	for(int T=n,i=1;i<=m;T-=a[i],i++) now=now*(ll)C[T][a[i]]%ha*(ll)jc[a[i]-1]%ha;
	for(int i=1;i<=n;i++) if(b[i]>1) now=now*(ll)ni[b[i]]%ha;
	
	ADD(ans,now*(ll)ci[tot]%ha);
}

void dfs(int x,int lef){
	if(lef<x){ if(!lef) calc(); return;}
	for(int i=0,u=0;u<=lef;i++,u+=x) b[x]=i,dfs(x+1,lef-u),b[x]=0;
}

int main(){	
	ci[0]=1; for(int i=1;i<ha;i++) ci[i]=add(ci[i-1],ci[i-1]);
	jc[0]=1; for(int i=1;i<=60;i++) jc[i]=jc[i-1]*(ll)i%ha;
	ni[60]=ksm(jc[60],ha-2);
	for(int i=60;i;i--) ni[i-1]=ni[i]*(ll)i%ha;	
	
	C[0][0]=1;
	for(int i=1;i<=60;i++){
		C[i][0]=1;
		for(int j=1;j<=i;j++) C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	}
	
	scanf("%d",&n);
	
	for(int i=0;i<=n;i++)
	    for(int j=0;j<=i;j++)
	        if(!i||!j) gcd[i][j]=i+j;
	        else gcd[j][i]=gcd[i][j]=gcd[j][i%j];
	
    dfs(1,n),ans=ans*(ll)ni[n]%ha;
	
	printf("%d\n",ans);
	return 0;
} 

  

[HNOI2009] 圖的同構