【模板】Polya 定理

给定一个(n)个点,(n)条边的环,有(n)种颜色,给每个顶点染色,问有多少种本质不同的染色方案,答案对(10^9+7)取模

注意本题的本质不同,定义为:只需要不能通过旋转与别的染色方案相同

一种染色方案可以看做先染了前(d)个位置,然后将这一段复制(frac{n}{d})次拼接而成,其中(d|n)。我们称这个(d)为一种染色方案的周期(这里默认是最小正周期)。

对于一个周期(循环个数)为(d)的染色方案,将它旋转(d)次会得到(d)个看起来不同但本质相同的染色方案。

(f(x))表示周期为(x)的看上去不同的方案数。枚举周期(d),那么每一种周期的方案就多算(d)次,因此(ans=sumlimits_{d|n}dfrac{f(d)}{d})

(f(x))不容易计算,但是我们知道:对于(n)的所有约数(d),将(f(d))求和就会得到总方案数(m^n),即

[m^n=sumlimits_{d|n}f(d) ]

莫比乌斯反演得

[f(x)=sumlimits_{d|x}mu(frac{n}{d})m^d ]

代入第一个式子得到

[ans=sumlimits_{d|n}frac{1}{d}sumlimits_{k|d}mu(frac{d}{k})m^k ]

[=sumlimits_{d|n}frac{d}{n}sumlimits_{k|frac{n}{d}}mu(frac{n}{dk})m^k ]

[=frac{1}{n}sumlimits_{d|n}dsumlimits_{k|frac{n}{d}}mu(frac{n}{dk})m^k ]

[=frac{1}{n}sumlimits_{k|n}m^ksumlimits_{d|frac{n}{k}}dmu(frac{n}{dk}) ]

[=frac{1}{n}sumlimits_{k|n}m^kvarphi(n/k) ]

上面设颜色的数量为(m)是为了避免混淆,本题中(m=n)

Code

#include <bits/stdc++.h>

using namespace std;

const int mod = 1e9 + 7;

int t, n, m;

unordered_map < int, int > fphi;

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10ll + ch - '0'; ch = getchar();}
	return x * fl;
}

int qpow(int base, int pw)
{
	int s = 1;
	while (pw)
	{
		if (pw & 1) s = 1ll * s * base % mod;
		base = 1ll * base * base % mod;
		pw >>= 1;
	}
	return s;
}

int phi(int x)
{
	if (fphi[x]) return fphi[x];
	int t = x, y = x;
	for (int i = 2; i <= m; i ++ )
	{
		if ((x % i) == 0)
		{
			t = t / i * (i - 1);
			while ((x % i) == 0) x /= i;
		}
	}
	if (x > 1) t = t / x * (x - 1);
	return fphi[y] = t;
}

int main()
{
	t = read();
	while (t -- )
	{
		n = read(), m = (int)(sqrt(n));
		int res = 0;
		for (int k = 1; k <= m; k ++ )
		{
			if (n % k) continue;
			res = (res + 1ll * qpow(n, k) * phi(n / k) % mod) % mod;
			if (k * k != n) res = (res + 1ll * qpow(n, n / k) * phi(k) % mod) % mod;
		}
		res = 1ll * res * qpow(n, mod - 2) % mod;
		printf("%d
", res);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/andysj/p/14582189.html