Color[POJ2154]

【题目描述】
一串项链上有(n)颗珠子,用(n)种颜色给它们染色(不一定都要用到),有多少种方案?通过已有方案进行旋转得到的染色方案不算。答案对(p)取模。

【输入格式】
两个整数(n,p)

【输出格式】
一行一个整数表示总方案数模(p)

(nle 10^9)

题解

(operatorname{Burnside}) 引理

(A)(B)为有限集合,(X)表示所有从(A)(B)的映射集合(着色方案集合)。(G)是一个置换群,(X/G)表示(X)所有元素的轨道的集合(去重),(X^g)表示(X)中在某个(G)中置换(g)作用下的不动元素集合,则有$$|X/G|=frac{1}{|G|}sumlimits_{gin G}|X^g|$$

看不懂没关系 用人话解释一下

比如给一个正方形的4个顶点染2种颜色

(X)是着色方案集合 这里先不考虑重复 每个点都有两种颜色可染 所以着色方案有(2^4=16)种 这里我们可以把这个正方形旋转0°,90°,180°或270°,所以(G)就是{旋转0°,旋转90°,旋转180°,旋转270°}。

先算一下(X^{g_1}) 也就是旋转0°后 16种方案里有多少种4个角的颜色相对没变的

然而旋转0°也就是不旋转 16种都不会变

(X^{g_2}) 旋转90°呢 只有2种 就是4个顶点颜色都一样的2种

(X^{g_3}=4) 旋转180°时 ( exttt{RRRR}quad exttt{BBBB}quad exttt{RBRB}quad exttt{BRBR})都是合法的

(X^{g_4})(X^{g_2})

所以此题答案是(frac{1}{4}(16+2+4+2)=6) !!!

但是集合(G)的大小是(n)级别的 集合(X)的大小更是指数级的 我们也不知道怎么计算(X^g)

于是我们需要用到 (operatorname{Polya}) 定理

先简单介绍一下什么是置换

[egin{pmatrix}1 & 2 & 3 & 4 \ 3 & 1 & 2 & 4 end{pmatrix} ]

这个置换是上下对应的 表示把 (1) 的颜色给 (3),把 (2) 的颜色给 (1),把 (3) 的颜色给 (2),把 (4) 的颜色给 (4)

所以如果把正方形的四个顶点顺时针顺序标为 (1,2,3,4)

那么顺时针旋转90°就对应这样的一个置换:

[egin{pmatrix}1 & 2 & 3 & 4 \ 2 & 3 & 4 & 1 end{pmatrix} ]

任意一个置换都能分解成若干个循环置换的乘积

什么又是循环置换?(不想写了放张图)

(egin{pmatrix}1 & 2 & 3 & 4 & 5 \ 3 & 1 & 2 & 5 & 4 end{pmatrix}) 就是这两个循环置换合在一起

这个循环置换 (egin{pmatrix}1 & 3 & 2 \ 3 & 2 & 1 end{pmatrix}) 按顺序排一下也就是 (egin{pmatrix}1 & 2 & 3 \ 3 & 1 & 2 end{pmatrix})
和这个循环置换 (egin{pmatrix}4 & 5 \ 5 & 4 end{pmatrix})

然后回到刚才那个引理 一个置换(g)可以表示为若干个循环置换的乘积 然而对于每个循环置换 这个循环置换里的每个元素 的颜色 必须全部一样 才能保证经过置换后每个元素的颜色都不变(这个不好理解)

(m)是颜色种类数 (c(g))(g)最多被分解成几个循环置换的乘积 那么有(|X^g|=m^{c(g)})

把这个代入刚才那个(operatorname{Burnside}) 引理

得到 (operatorname{Polya}) 定理:

[|X/G|=frac{1}{|G|}sumlimits_{gin G}m^{c(g)} ]

看一下刚才那个正方形的例子
(g_1)(egin{pmatrix}1 & 2 & 3 & 4 \ 1 & 2 & 3 & 4 end{pmatrix})
那么 (c(g_1))(4) 总之自己算一下得到 (c(g_2)=1)(c(g_3)=2)(c(g_4)=1)
答案就和刚才一样 (frac{1}{4}(2^4+2^1+2^2+2^1)=6)

那么这题怎么办呢 集合 (G) 很容易找 就是顺时针旋转过 (1sim n) 个珠子(旋转过 (n) 个就是没旋转) 所以 (|G|=n)

找找规律 发现顺时针旋转 (i) 个珠子得到的置换最多能被分解成 (gcd(i,n)) 个循环置换(原因我也忘了)

所以枚举 (n) 的因数 (d) 表示 (gcd(i,n)=d) 那么一定有 (gcd(frac{i}{d},frac{n}{d})=1)(frac{i}{d}le frac{n}{d})

所以 (gcd(i,n)=d)(1le ile n) 的个数就是 (varphi(frac{n}{d})) (欧拉函数)

答案就是 $$frac{1}{n}sumlimits_{d|n}n^dvarphi(frac{n}{d})$$

预处理一下质数 时间复杂度据说是(O(n^{0.75}))

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

int ttt, n, mod, tot;
int prime[40005], pr;
bool np[40005];

inline int read() {
	int x = 0, f = 1; char ch = getchar();
	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
	return x * f;
}

void write(int x) {
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

inline void getprime() {
	for (int i = 2; i <= 36000; i++) {
		if (!np[i]) {
			prime[++pr] = i;
		}
		for (int j = 1; j <= pr && (i * prime[j] <= 36000); j++) {
			np[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

inline int getphi(int x) {
	int ret = x;
	for (int i = 1; prime[i] * prime[i] <= x; i++) {
		if (x % prime[i] == 0) {
			ret = ret - ret / prime[i];
			while (x % prime[i] == 0) x /= prime[i];
		}
	}
	if (x > 1) {
		ret = ret - ret / x;
	}
	return ret % mod;
}

inline int fpow(int x, int t) {
	int ret = 1; x %= mod;
	for (; t; t >>= 1, x = x * x % mod) if (t & 1) ret = ret * x % mod;
	return ret;
}

inline int solve(int x) {
	int ret = 0;
	for (int i = 1; i * i <= x; i++) {
		if (x % i == 0) {
			ret = (ret + fpow(x, i - 1) * getphi(x / i) % mod) % mod;
			if (x != i * i) {
				ret = (ret + fpow(x, x / i - 1) * getphi(i) % mod) % mod;
			}
		}
	}
	return ret;
}

int main() {
	ttt = read();
	getprime();
	while (ttt--) {
		n = read(); mod = read();
		write(solve(n)); puts("");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM39.html