AHOI 2002 黑白瓷砖

来源:互联网 发布:美国新纳粹知乎 编辑:程序博客网 时间:2024/06/10 17:24

小可可在课余的时候受美术老师的委派从事一项漆绘瓷砖的任务。首先把n(n+1)/2块正六边形瓷砖拼成三角形的形状,右图给出了n=3时拼成的“瓷砖三角形”。然后把每一块瓷砖漆成纯白色或者纯黑色,而且每块瓷砖的正、反两面都必须漆成同样的颜色。 

有一天小可可突发奇想,觉得有必要试试看这些瓷砖究竟能够漆成多少种本质不同的图案。所谓两种图案本质不同就是其中的一种图案无论如何旋转、或者翻转、或者同时旋转和翻转都不能得到另外一种图案。

旋转是将瓷砖三角形整体顺时针旋转120度或240度。

翻转是将瓷砖三角形整体左右翻动180度。


一开始,小可可觉得这项实验很有意思,他知道n=2时有两个本质不同的漆绘方案,n=4时也只有四个本质不同的漆绘方案。小可可还把这些漆绘方案画了出来。

 但是后来小可可发现在变大的过程中,漆绘方案的数目增长很快,在n=14的时候,居然有6760803201217259503457555972096种不同的漆绘方案。这果然是一项非常艰巨的实验。因此他决定请你编写程序帮他求解本质不同的漆绘方案数

一个正整数n, n≤20

一行正整数,代表问题的解s。

输入1: 1

输入2: 2

输出1:2

输出2:4

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 + 5int 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;}


0 0
原创粉丝点击