1. 程式人生 > >洛谷 P4128: bzoj 1815: [SHOI2006]有色圖

洛谷 P4128: bzoj 1815: [SHOI2006]有色圖

排列 tdi 好題 直接 多重 clu amp 一個點 long long

題目傳送門:洛谷 P4128。

計數好題,原來是 13 年前就出現了經典套路啊。這題在當年應該很難吧。

題意簡述:

\(n\) 個點的完全圖,點沒有顏色,邊有 \(m\) 種顏色,問本質不同的圖的數量對質數 \(p>n\) 取模。

本質不同指的是在點的 \(n!\) 種不同置換下不同。

題解:

首先有 \(\mathrm{P\acute{o}lya}\) 定理:一類元素在一個置換群的作用下本質不同的元素(不同等價類)個數等於 \(\frac{1}{|G|}\sum_{g\in G}M(g)\)。其中 \(G\) 是所有置換的集合,\(M(g)\) 是一個置換的不動點個數。

那麽我們考慮一個點的置換 \((a_1,a_2,\ldots,a_n)\)

,因為一個置換可以拆分成不相交循環的乘積,考慮這個置換能拆分成 \(k\) 個長度分別為 \(b_1,b_2,\ldots,b_k\) 的不相交循環的乘積。顯然它的不動點個數為 \(m\) 的在這個置換下邊的等價類個數次方。如何計算它的邊等價類個數?

我們考慮兩類邊,第一類是端點在同一循環中的邊,第二類是端點在不同循環中的邊。

對於第一類邊,考慮一個長度為 \(b\) 的循環。把 \(b\) 個點按順序等距分布在一個圓上,在循環作用下每條邊都會往順時針方向位移一格。則容易得到兩條邊處於不同等價類當且僅當它們長度不同,可以得出長度為 \(b\) 的循環內部共有 \(\lfloor\frac{b}{2}\rfloor\)

種邊的等價類。

對於第二類邊,考慮兩個長度分別為 \(b_1\)\(b_2\) 的不同循環。對於一條邊,在這個置換的重復作用下經過 \(\mathrm{lcm}(b_1,b_2)\) 次操作會回到自身。所以每條邊的在一個大小為 \(\mathrm{lcm}(b_1,b_2)\) 的等價類中,則不同等價類的個數為 \(\frac{b_1b_2}{\mathrm{lcm}(b_1,b_2)}=\gcd(b_1,b_2)\)

綜合一下,得到等價類個數為 \(\sum_{i}\lfloor\frac{b_i}{2}\rfloor+\sum_{i<j}\gcd(b_i,b_j)\)。對於一個已經求出 \(b\)

的置換,這個式子可以在 \(\Theta(k^2\log b_i)\) 的時間內求出。

接下來需要對所有置換統計,顯然我們不能枚舉每個置換,但是可以發現,\(b\) 一樣的置換答案也相同。考慮枚舉 \(b\),即本質不同的不相交循環長度的可重集。這相當於枚舉 \(n\) 的每個整數分拆。本題中 \(n\le 53\),而 \(53\) 的整數分拆數量也不大。

考慮枚舉了 \(b_1\le b_2\le \cdots\le b_k\),其中 \(\sum b_i=n\),如何計算它對應了多少種不同的置換呢?

考慮對於 \(1\)\(n\) 的每個點,分配它在第幾個置換中,這相當於一個多重組合數 \(\frac{n!}{\prod b_i!}\)。而對於每一個置換,分配它內部的順序,這相當於一個圓排列,即 \(\prod (b_i-1)!\),結合前面是 \(\frac{n!}{\prod b_i}\)。最後我們會發現這樣其實會算重,因為每個循環的前後順序不影響,不能把 \(1,2\)\(2,1\)(這裏表示每個點在第幾個循環內)當作不同的循環分配方案。發現只有 \(b_i\) 相同的會影響,假設有 \(c\) 個,正好多乘了 \(c!\) 種方案,除掉就好了。這相當於 \(\frac{n!}{\prod b_i\prod c!}\)

發現因為有 \(|G|=n!\) 種置換,正好和最後 \(\frac{1}{|G|}\) 抵消了,所以最終的式子是:\(\sum_{b}\frac{\sum_{i}\lfloor\frac{b_i}{2}\rfloor+\sum_{i<j}\gcd(b_i,b_j)}{\prod b_i\prod c!}\)

對於枚舉每一種 \(b\),可以直接 DFS。而後面兩個東西都是能直接在 DFS 過程中計算的,優化常數。

本題很貼心地保證 \(p>n\),可以放心求階乘和階乘逆元。

時間復雜度大約是 \(O(\log n\sum_{p\in\mathrm{Partition}(n)}\mathrm{len}^{2}(p))\),實際比較小,更多可以查看 A296010。

#include <cstdio>
#include <algorithm>

typedef long long LL;
const int MN = 60;

int N, M, P, Sum;
inline int qPow(int b, int e) {
    int a = 1;
    for (; e; b = (LL)b * b % P, e >>= 1)
        if (e & 1) a = (LL)a * b % P;
    return a;
}

int Inv[MN], Fac[MN], iFac[MN];
inline void Init(int N) {
    Inv[1] = 1;
    for (int i = 2; i <= N; ++i) {
        Inv[i] = (LL)(P - P / i) * Inv[P % i] % P;
    }
    Fac[0] = iFac[0] = 1;
    for (int i = 1; i <= N; ++i) {
        Fac[i] = (LL)Fac[i - 1] * i % P;
        iFac[i] = (LL)iFac[i - 1] * Inv[i] % P;
    }
}

int stk[MN], t, n1, n2 = 1;
void DFS(int s, int mx, int c) {
    if (!s) {
        Sum = (Sum + (LL)qPow(M, n1) * n2) % P;
        return ;
    }
    int a = n1, b = n2;
    for (int i = 1; i <= mx; ++i) {
        stk[++t] = i;
        n1 = a + i / 2;
        for (int j = 1; j < t; ++j) n1 += std::__gcd(stk[j], i);
        n2 = (LL)b * Inv[i] % P;
        if (i == stk[t - 1]) n2 = (LL)n2 * Fac[c] % P * iFac[c + 1] % P;
        DFS(s - i, std::min(s - i, i), i == stk[t - 1] ? c + 1 : 1);
        --t;
    }
}

int main() {
    scanf("%d%d%d", &N, &M, &P);
    Init(N);
    DFS(N, N, 0);
    printf("%d\n", Sum);
    return 0;
}

聽說有色圖?來了來了!色圖在哪裏啊?

洛谷 P4128: bzoj 1815: [SHOI2006]有色圖