1. 程式人生 > >【BZOJ1488】[HNOI2009]圖的同構(Burside引理,Polya定理)

【BZOJ1488】[HNOI2009]圖的同構(Burside引理,Polya定理)

兩個 選擇 設有 就是 距離 總數 fin 一個 不存在

【BZOJ1488】[HNOI2009]圖的同構(Burside引理,Polya定理)

題面

BZOJ
洛谷

題解

求本質不同的方案數,很明顯就是群論這套理論了。
置換一共有\(n!\)個,考慮如何對於任意一個置換求不動點數量。
首先邊存在或者不存在太麻煩了,我們假裝所有邊都已經存在,出現過的邊和不存在的邊用兩種不同的顏色染色即可。這樣子我們就假裝所有的邊都出現了,也就是一個完全圖。
顯然循環是對於點而論的,但是這題同構是對於邊而論的。那麽我們對於一個點的循環,考慮它的兩個頂點。這兩個頂點只有兩種不同情況,要麽在同一個循環內,要麽不在同一個循環內。考慮所有在同一個循環中的\(n\)點形成的完全圖,那麽它的邊構成了\(n/2\)

個循環,感性理解就是,我們把\(n\)個點拉成一排,把相鄰距離為定值的點連上邊,顯然這樣子會構成一個環,因為這個距離的定值\(d\)\(n-d\)是等價的,所以邊構成了\(n/2\)個循環。
考慮兩個頂點不在一個循環內,那麽構成循環必定是在一個點集中選擇一條邊連向另外一個點集,再從另外一個點集中選一條邊連回來,我們把點集看成兩個環,那麽每次可以把環上所有的點旋轉一下,那麽旋轉\(lcm\)次之後就轉回來了,意味著這\(lcm\)條邊必須相同,即構成一個循環,那麽邊的循環的個數就是\(gcd\)了。
假設有邊的置換,我們很容易知道不動點的數量就是\(2^m\),其中\(m\)是邊置換的數量,顯然你的個置換中的邊的顏色都是相同的。
這樣一來,我們就把點置換轉換為邊置換了,這樣就可以方便的計算了。
\(60\)的拆分數大概是百萬級別的,我們似乎是可以爆搜拆分數計算答案的。
那麽考慮一個拆分數實際上對應的方案數,這個排列組合計算一下就好了。
我們假設有\(k\)個循環,每個的大小分別是\(a_1,a_2,...\),每個大小的置換個數是\(num_1,num_2...\)
那麽這種情況的貢獻就是\(\frac{n!}{(\prod a_i!)*(\prod num_i!)}\),原因就是,\(n!\)是所有方案,然後對於每個置換內,顯然環的位置不影響,出去等價的環,然後對於每個等大小的置換,顯然位置是可以交換的,那麽除去階乘的排列的影響。

好了,這樣子大概可以算完所有置換的不動點數量,然後除掉一個置換總數就對了,顯然置換總數是\(n!\)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 997
#define MAX 75
int n,ans,jc[MOD],inv[MOD],jv[MOD];
int g[MAX][MAX],a[MAX],b[MAX],bin[MAX*MAX];
void calc()
{
    int ret=jc[n],tot=0,sum=0;
    for(int i=1;i<=n;++i)
    {
        ret=ret*jv[a[i]]%MOD;
        for(int j=1;j<=a[i];++j)
            ret=ret*inv[i]%MOD,b[++tot]=i;
    }
    for(int i=1;i<=tot;++i)sum+=b[i]/2;
    for(int i=1;i<=tot;++i)
        for(int j=i+1;j<=tot;++j)
            sum+=g[b[i]][b[j]];
    ret=ret*bin[sum]%MOD;
    ans=(ans+ret)%MOD;
}
void dfs(int x,int sum)
{
    if(x==1){a[x]=n-sum;calc();return;}
    for(int i=0;sum+i*x<=n;++i)
        a[x]=i,dfs(x-1,sum+i*x);
}
int main()
{
    scanf("%d",&n);
    jc[0]=jv[0]=inv[0]=inv[1]=bin[0]=1;
    for(int i=2;i<MOD;++i)inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
    for(int i=1;i<MOD;++i)jc[i]=jc[i-1]*i%MOD;
    for(int i=1;i<MOD;++i)jv[i]=jv[i-1]*inv[i]%MOD;
    for(int i=1;i<MAX*MAX;++i)bin[i]=bin[i-1]*2%MOD;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            g[i][j]=__gcd(i,j);
    dfs(n,0);ans=ans*jv[n]%MOD;
    printf("%d\n",ans);return 0;
}

【BZOJ1488】[HNOI2009]圖的同構(Burside引理,Polya定理)