小可可在课余的时候受美术老师的委派从事一项漆绘瓷砖的任务。首先把n(n+1)/2块正六边形瓷砖拼成三角形的形状,右图给出了n=3时拼成的“瓷砖三角形”。然后把每一块瓷砖漆成纯白色或者纯黑色,而且每块瓷砖的正、反两面都必须漆成同样的颜色。
有一天小可可突发奇想,觉得有必要试试看这些瓷砖究竟能够漆成多少种本质不同的图案。所谓两种图案本质不同就是其中的一种图案无论如何旋转、或者翻转、或者同时旋转和翻转都不能得到另外一种图案。
旋转是将瓷砖三角形整体顺时针旋转120度或240度。
翻转是将瓷砖三角形整体左右翻动180度。
一开始,小可可觉得这项实验很有意思,他知道n=2时有两个本质不同的漆绘方案,n=4时也只有四个本质不同的漆绘方案。小可可还把这些漆绘方案画了出来。但是后来小可可发现在变大的过程中,漆绘方案的数目增长很快,在n=14的时候,居然有6760803201217259503457555972096种不同的漆绘方案。这果然是一项非常艰巨的实验。因此他决定请你编写程序帮他求解本质不同的漆绘方案数
输入描述 Input Description
一个正整数n, n≤20
输出描述 Output Description
一行正整数,代表问题的解s。
样例输入 Sample Input
输入1: 1
输入2: 2
样例输出 Sample Output
输出1:2
输出2:4
数据范围及提示 Data Size & Hint
s不超过200位
题解:
本题需用到计算置换群中本质不同的方案的Polya定理。
本题提到了三个置换:旋转120°,旋转240°,以及左右翻转。
但只有这三个置换能否构成置换群呢?
显然不行,有以下两个问题:
1、无幺元素:很简单,再加一个“不动”置换就可以了。
2、不满足封闭性:很简单,再加两个斜着的翻转就可以了。
好的,我们现在就构造出了一个置换群。
根据Polya定理:ans = (sigma(2^c(ai))) / n。
c(ai)表示在ai置换下的置换子环的个数,n是置换的总个数,然后我们也可以得到:
1、“不动”置换的子环个数显然就是格子数。令格子数为t。
2、旋转的子环个数就是(格子数 / 3)(向上取整) 【自己脑补一下。。。其实挺显然的】令此为x
3、翻转的子环个数就是((格子数 + (n / 2)(向上取整)) / 2)(不用取整,这一定是整数)令此为y
对于第3点,稍稍解释一下:中轴线上会有(n / 2)(向上取整)个格子,所以我们要把总格子数加上这个再去除以2。
所以ans = (2 ^ t + 2 * 2 ^ x + 3 * 2 ^ y) / 6; 【有一个不动置换,两个旋转,三个翻转,总共6个置换】
就这么简单
代码:
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 250 + 5 int n; struct GMH { int num[N]; GMH operator + (GMH a) { GMH b; memset(b.num, 0, sizeof(b.num)); b.num[0] = max(num[0], a.num[0]); for (int i = 1; i <= b.num[0]; i ++) { b.num[i] += num[i] + a.num[i]; if (i < b.num[0]) { b.num[i + 1] += b.num[i] / 10; b.num[i] %= 10; } } while (b.num[b.num[0]] >= 10) { b.num[b.num[0] + 1] += b.num[b.num[0]] / 10; b.num[b.num[0] ++] %= 10; } return b; } GMH operator * (int k) { GMH b; memset(b.num, 0, sizeof(b.num)); b.num[0] = num[0]; for (int i = 1; i <= b.num[0]; i ++) b.num[i] = num[i] * k; for (int i = 1; i < b.num[0]; i ++) { b.num[i + 1] += b.num[i] / 10; b.num[i] %= 10; } while (b.num[b.num[0]] >= 10) { b.num[b.num[0] + 1] += b.num[b.num[0]] / 10; b.num[b.num[0] ++] %= 10; } return b; } GMH operator / (int k) { GMH b; memset(b.num, 0, sizeof(b.num)); for (int i = num[0]; i; i --) { if (num[i] >= k) { b.num[i] = num[i] / k; num[i] %= k; b.num[0] = max(b.num[0], i); } if (i > 1) { num[i - 1] += 10 * num[i]; num[i] = 0; } } return b; } void out() { for (int i = num[0]; i; i --) printf("%d", num[i]); printf("\n"); } }Num, fac[N]; void begin() { scanf("%d", &n); fac[0].num[0] = fac[0].num[1] = 1; } void work() { int t = n * (n + 1) / 2; int x = t / 3 + (t % 3 ? 2 : 1); int y = t + (n + 1 >> 1) >> 1; int maxn = max(max(x, y), t); for (int i = 1; i <= maxn; i ++) fac[i] = fac[i - 1] * 2; Num = (fac[t] + fac[x] + fac[y] * 3) / 6; } void end() { Num.out(); } int main() { begin(); work(); end(); return 0; }