1. 程式人生 > 實用技巧 >題解-JSOI2011 同分異構體計數

題解-JSOI2011 同分異構體計數

題面

JSOI2011 同分異構體計數

求有多少個本質不同的無向圖基環樹,有 \(n\) 個點且環上的點數 \(\le m\)。答案對 \(p\) 取模。

資料範圍:\(3\le n\le 1000\)\(3\le m\le 50\)\(10^4\le p\le 2\times 10^9\)


題解

\(t(x)\) 為根節點子節點數 \(\le 2\),所有節點子節點數 \(\le 3\) 的本質不同無標號有根樹個數。

這個東西不需要多項式,直接 Burnside 定理然後 dp 即可,參考 烷基計數 加強版

然後列舉環長,設為 \(k\),設當前答案多項式為 \(\frac{f(x)}{2k}\)

利用 Burnside 定理,考慮 \(G\) 的元素:

  1. 不動,共 \(1\) 種。\(f(x)\leftarrow t(x)^k\)

  2. 翻轉,共 \(k\) 種(即環不考慮外向樹的對稱軸個數,想象一下翻轉後的置換環數即可)。

    • 假設 \(k\) 是奇數 \(f(x)\leftarrow t(x^2)^{\lfloor k/2\rfloor}t(x)\)
    • 假設 \(k\) 是偶數 \(f(x)\leftarrow \frac{1}{2}(t(x^2)^{k/2}+t(x^2)^{k/2-1}t(x)^2)\)
  3. 旋轉,共 \(k-1\) 種。設旋轉節為 \(d\),設 \(g=\gcd(d,k)\)

    \(f(x)\leftarrow t(x^{k/g})^{g}\)

\(n,m\) 很小,這部分不用多項式也可以實現:

\(\Theta(n^2 m)\) 暴力卷積預處理出 \(t(x)^a\)

上面的 \(t(x^s)^a[x^n]=t(x)^a[x^{n/s}]\),也可以求了。

總時間複雜度 \(\Theta(n^2 m+m^2\log m)\)


程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);i>=0;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1001,M=51;
int n,m,mod,f[N],g[N],t[N],p[M][N];

//Math
int& fmod(int &x){return x+=x>>31&mod;}
int gcd(int a,int b){return a?gcd(b%a,a):b;}
int mypow(int a,int x=mod-2,int res=1){
    for(;x;x>>=1,a=1ll*a*a%mod)
        (x&1)&&(res=1ll*res*a%mod);
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>mod,f[0]=p[0][0]=1;
    const int i6=mypow(6),i2=mypow(2);
    for(int i=1;i<=n;i++){
        R(a,i)  g[i]=(1ll*f[a]*f[i-1-a]+g[i])%mod;
        R(a,i)  f[i]=(1ll*f[a]*g[i-a]+f[i])%mod;
        for(int a=0;(a<<1)<i;a++)
            f[i]=(3ll*f[a]%mod*f[i-1-(a<<1)]+f[i])%mod;
        if((i-1)%3==0) f[i]=(2ll*f[(i-1)/3]+f[i])%mod;
        f[i]=1ll*f[i]*i6%mod,t[i]=g[i];
        if(i&1) t[i]=(f[(i-1)>>1]+t[i])%mod;
        t[i]=1ll*t[i]*i2%mod;
        // cout<<"i="<<i<<" t="<<t[i]<<'\n';
    }
    R(k,m)R(i,n+1)R(j,n+1-i)
        p[k+1][i+j]=(1ll*p[k][i]*t[j]+p[k+1][i+j])%mod;
    int ns=0;
    for(int k=3;k<=m;k++){
        int res=p[k][n]; // cout<<res<<'\n';
        if(k&1) for(int a=0;(a<<1)<=n;a++)
            res=(1ll*k*p[k>>1][a]%mod*t[n-(a<<1)]+res)%mod;
        else {
            if(~n&1) res=(1ll*(k>>1)*p[k>>1][n>>1]+res)%mod;
            for(int a=0;(a<<1)<=n;a++)
                res=(1ll*(k>>1)*p[(k>>1)-1][a]%mod
                    *p[2][n-(a<<1)]+res)%mod;
        }
        for(int d=1,G;d<k;d++)if(n%(k/(G=gcd(d,k)))==0)
            res=(0ll+p[G][n/(k/G)]+res)%mod;
        ns=(1ll*res*mypow(k<<1)+ns)%mod;
    }
    cout<<ns<<'\n';
    return 0;
}

祝大家學習愉快!