题解 P4980 【【模板】Polya定理】

GKxx

2019-07-28 00:09:07

Solution

### 题意 $n$个点的环,用$m$种颜色给点染色,求本质不同的方案数。 两个方案本质相同,当且仅当其中一个方案可以通过旋转得到另一个。 ### 题解 一种染色方案可以看做先染了前$d$个位置,然后将这一段复制$n/d$次拼接而成,其中$d$是$n$的约数。我们称这个$d$为一种染色方案的*周期*(这里默认是最小正周期)。 对于一个周期为$d$的染色方案,将它旋转$d$次会得到$d$个看起来不同但本质相同的染色方案。 设$f(x)$表示周期为$x$的看上去不同的方案数。枚举周期$d$,那么每一种周期的方案就多算了$d$次,因此 $$ans=\sum\limits_{d|n}\frac{f(d)}{d}$$ $f(x)$不容易计算,但是我们知道:对于$n$的所有约数$d$,将$f(d)$求和就会得到总方案数$m^n$,即 $$m^n=\sum\limits_{d|n}f(d)$$ 莫比乌斯反演得 $$f(x)=\sum\limits_{d|x}\mu(x/d)m^d$$ 代入第一个式子得到 $$ans=\sum\limits_{d|n}\frac{1}{d}\sum\limits_{k|d}\mu(d/k)m^k$$ $$=\sum\limits_{d|n}\frac{d}{n}\sum\limits_{k|\frac{n}{d}}\mu(n/dk)m^k$$ $$=\frac{1}{n}\sum\limits_{d|n}d\sum\limits_{k|\frac{n}{d}}\mu(n/dk)m^k$$ $$=\frac{1}{n}\sum\limits_{k|n}m^k\sum\limits_{d|\frac{n}{k}}d\mu(n/dk)$$ $$=\frac{1}{n}\sum\limits_{k|n}m^k\varphi(n/k)$$ 上面设颜色的数量为$m$是为了避免混淆,本题中$m=n$。 本题对于等价类的定义是相当简单的,因此可以用莫比乌斯反演轻松解决。如果遇到更加复杂的等价类定义就要老老实实使用polya计数了。 ```cpp #include <cctype> #include <cstdio> #include <climits> #include <algorithm> template <typename T> inline void read(T& x) { int f = 0, c = getchar(); x = 0; while (!isdigit(c)) f |= c == '-', c = getchar(); while (isdigit(c)) x = x * 10 + c - 48, c = getchar(); if (f) x = -x; } template <typename T, typename... Args> inline void read(T& x, Args&... args) { read(x); read(args...); } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + 48); } template <typename T> inline void writeln(T x) { write(x); puts(""); } template <typename T> inline bool chkmin(T& x, const T& y) { return y < x ? (x = y, true) : false; } template <typename T> inline bool chkmax(T& x, const T& y) { return x < y ? (x = y, true) : false; } typedef long long LL; const LL mod = 1e9 + 7; inline LL qpow(LL x, LL k) { LL s = 1; for (; k; x = x * x % mod, k >>= 1) if (k & 1) s = s * x % mod; return s; } inline LL phi(LL x) { LL ans = x; for (LL i = 2; i * i <= x; ++i) if (!(x % i)) { ans = ans / i * (i - 1); while (!(x % i)) x /= i; } if (x > 1) ans = ans / x * (x - 1); return ans; } int main() { int T; read(T); while (T--) { LL n; read(n); LL ans = 0; for (LL k = 1; k * k <= n; ++k) if (!(n % k)) { if (k * k == n) ans = (ans + qpow(n, k) * phi(n / k) % mod) % mod; else { ans = (ans + qpow(n, k) * phi(n / k) % mod) % mod; ans = (ans + qpow(n, n / k) * phi(k) % mod) % mod; } } writeln(ans * qpow(n, mod - 2) % mod); } return 0; } ```