15年国赛 模型染色

标题:模型染色

在电影《超能陆战队》中,小宏可以使用他的微型机器人组合成各种各样的形状。
现在他用他的微型机器人拼成了一个大玩具给小朋友们玩。为了更加美观,他决定给玩具染色。

小宏的玩具由n个球型的端点和m段连接这些端点之间的边组成。下图给出了一个由5个球型端点和4条边组成的玩具,看上去很像一个分子的球棍模型。
 
由于小宏的微型机器人很灵活,这些球型端点可以在空间中任意移动,同时连接相邻两个球型端点的边可以任意的伸缩,这样一个玩具可以变换出不同的形状。在变换的过程中,边不会增加,也不会减少。
小宏想给他的玩具染上不超过k种颜色,这样玩具看上去会不一样。如果通过变换可以使得玩具变成完全相同的颜色模式,则认为是本质相同的染色。现在小宏想知道,可能有多少种本质不同的染色。

【输入格式】
输入的第一行包含三个整数n, m, k,
分别表示小宏的玩具上的端点数、边数和小宏可能使用的颜色数。端点从1到n编号。
接下来m行每行两个整数a, b,表示第a个端点和第b个端点之间有一条边。输入保证不会出现两条相同的边。

【输出格式】
输出一行,表示本质不同的染色的方案数。由于方案数可能很多,请输入方案数除10007的余数。

【样例输入】
3 2 2
1 2
3 2
【样例输出】
6
【样例说明】
令(a, b, c)表示第一个端点染成a,第二个端点染成b,第三个端点染成c,则下面6种本质不同的染色:(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 2), (2, 2, 2)。
而(2, 1, 1)与(1, 1, 2)是本质相同的,(2, 2, 1)与(1, 2, 2)是本质相同的。

【数据规模与约定】
对于20%的评测数据,1<=n<=5, 1<=k<=2。
对于50%的评测数据,1<=n<=10, 1<=k<=8。
对于100%的评测数据,1<=n<=10, 1<=m<=45, 1<=k<=30。

资源约定:
峰值内存消耗 < 512M
CPU消耗  < 5000ms

是一个比较复杂的计数问题,需要使用多种颜色对图进行染色。
如果使用搜索方法,可以获得本题比较基础的分,搜索比较复杂,需要判断图的同构。
一种正确的做法是使用组合数学的Polya定理。这个定理需要对于给定的图求出对于这个图的所有置换。
可以使用枚举所有可能的排列,判断这个排列是否与原图同构来判断是否是一个置换。
求完置换后求出每个置换循环节数量,利用Polya定理可以求解。
需要用到比较复杂的知识,同构的判断容易出错。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>

using namespace std;

const int MAXN = 100;
const int MOD = 10007;
int n, m, k;
int g[MAXN][MAXN];
int mp[MAXN], q[MAXN];
long long tt[MAXN];
long long pw[MAXN];

bool isIsomorphism(int p)
{
	for (int i = 1; i < p; ++i)
		if (g[p][i] ^ g[q[p]][q[i]])
			return false;
	return true;
}

void isomorphismSearch(int p, int cnt)
{
	if (p > n)
	{
		tt[cnt]++;
		return ;
	}
	for (int i = p; i <= n; ++i)
	{
		swap(q[p], q[i]);
		if (isIsomorphism(p))
		{
			int r = q[p];
			while (r<p)
				r = q[r];
			isomorphismSearch(p+1, cnt+(r==p));
		}
		swap(q[p], q[i]);
	}
}

void solve(long long a, long long b, long long c, long long &x, long long &y)
{
	if (b==0)
	{
		x = c/a; y = 0;
		return ;
	}
	solve(b, a%b, c, y, x);
	y -= a/b * x;
}

int main()
{
	cin >> n >> m >> k;
	for (int i = 0; i < m; ++i)
	{
		int a, b;
		cin >> a >> b;
		g[a][b] = g[b][a] = 1;
	}
	for (int i = 1; i <= n; ++i)
		q[i] = i;
	pw[0] = 1;
	for (int i = 1; i <= n; ++i)
		pw[i] = (pw[i-1]*k) % MOD;

	memset(tt, 0, sizeof(tt));
	isomorphismSearch(1, 0);
	long long ans = 0, stt = 0;
	for (int i = 1; i <= n; ++i)
	{
		ans += ((tt[i]%MOD)*pw[i])%MOD;
		stt += tt[i];
	}
	ans %= MOD;
	long long x, y;
	solve(stt%MOD, MOD, ans, x, y);
	x %= MOD;
	if (x < 0)
		x += MOD;
	cout << x << endl;
	return 0;
}


 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值