AHOI 2002 黑白瓷砖

小可可在课余的时候受美术老师的委派从事一项漆绘瓷砖的任务。首先把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 + 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值