洛谷 P4128: bzoj 1815: [SHOI2006]有色圖
題目傳送門:洛谷 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)\)
我們考慮兩類邊,第一類是端點在同一循環中的邊,第二類是端點在不同循環中的邊。
對於第一類邊,考慮一個長度為 \(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\)
接下來需要對所有置換統計,顯然我們不能枚舉每個置換,但是可以發現,\(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]有色圖