洛谷P4980 【模板】Polya 定理 题解
题目链接:P4980 【模板】Polya 定理
题意:
给定一个 $n$ 个点,$n$ 条边的环,有 $n$ 种颜色。
给每个顶点染色,问有多少种本质不同的染色方案,答案对 $10^9+7$ 取模
注意本题的本质不同,定义为:只需要不能通过旋转与别的染色方案相同。
输入格式:
第一行输入一个 $T$,表示有 $T$ 组数据
第二行开始,一共 $T$ 行,每行一个整数 $n$,意思如题所示。
输出格式:
共 $T$ 行,每行一个数字,表示染色方案数对 $10^9+7$ 取模后的结果。
数据范围:
$1\le T \le 10^3, ~1 \le n \le 10^9$
传送门:Pólya 定理 (在这篇文章里讲解了推导过程)
首先先把 Burnside 引理复习一下。
Burnside 引理
设 $A,B$ 为有限集合,$X$ 为一些从 $A$ 到 $B$ 的映射组成的集合(这可以视作直接染色)。
$G$ 是 $A$ 上的置换群,且 $X$ 中的映射在 $G$ 中的置换作用下封闭。
$X / G$ 表示 $G$ 作用在 $X$ 上产生的所有等价类的集合1(即本质相同的染色方案),则
其中 $X^g=\{x \mid x \in X,~g(x)=x\}$ ,也就是在置换 $g$ 下的不动点构成的集合。
考虑原题中的旋转操作,其对应 Burnside 引理中的置换群 $G$ ,而 $X$ 即为染色操作。
因此我们需要找到对于置换「 $g=$ 旋转 $k$ 次」,它的不动点有几个(也就是旋转 $k$ 下后本质不变的染色方案)
显然旋转 $k$ 次不动意味着染色方案存在长为 $a(a \mid k)$ 的循环节。
又因为 $a \mid n$ ,所以 $a \mid \gcd(k,n)$ ,于是我们可以改为判定存在一个长度为 $\gcd(k,n)$ 的循环节。
对于 $g=$ 旋转 $k$ 次,实际上每个子串前 $\gcd(k,n)$ 格都是可以任意取的,因此贡献为 $n^{\gcd(k, n)}$ 。
于是答案即为
换一个枚举方式
用一下莫比乌斯反演的套路
另外在本题中可以直接暴力求 $\varphi$ ,时间复杂度为 $\mathcal{O}(Tn^{\frac{3}{4}})$ 。
到这里其实就完成这道题了,不过我们从推导中可以进一步得到 Pólya 定理。
我们把置换 $g$ 写作如下形式
考虑将 $i$ 与 $a_i$ 连边,显然全部操作后会出现若干个环,并且这些环的颜色是相同的。
于是我们可以得到 Pólya 定理:
在与 Burnside 引理相同的前置条件下, 若 $X$ 为所有从 $A$ 到 $B$ 的映射,则
其中 $c(g)$ 表示置换 $g$ 能拆分成的不相交的循环置换的数量(也就是我们刚才说的那些环的数量)
好好好,代码在这:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
const int mod = 1e9 + 7;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
void add(int &x, int y) { (x += y) >= mod ? x -= mod : 0; }
#define N ((int)())
int qpow(int a, int b)
{
int r = 1;
while(b)
{
if(b & 1) r = r * a % mod;
b >>= 1; a = a * a % mod;
}
return r;
}
int phi(int n)
{
int ans = n;
for(int i = 2; i * i <= n; i++)
if(n % i == 0)
{
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
if(n > 1) ans = ans / n * (n - 1);
return ans;
}
void solve()
{
int n, res = 0; cin >> n;
for(int i = 1; i * i <= n; i++)
if(n % i == 0)
{
add(res, phi(i) * qpow(n, n / i) % mod);
if(i * i != n) { add(res, phi(n / i) * qpow(n, i) % mod); }
}
cout << res * qpow(n, mod - 2) % mod << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int qwq; cin >> qwq; while(qwq--) solve();
return 0;
}
参考文献:
[1] https://www.luogu.com.cn/blog/_post/214705
1. 若 $X$ 中的两个映射经过 $G$ 中的置换作用后相等,则它们在同一等价类中 ↩