[蓝桥杯 2015 国 B] 模型染色

给定 \(n\) 个点 \(m\) 条边的无向图 \(G\),求 \(k\) 染色的本质不同的方案数,对 \(10007\) 取模。

\(1 \leq n \leq 10\)\(1 \leq m \leq 45\)\(1 \leq k \leq 30\)

Pólya 定理的直接应用。

考虑一个变换 \(r\) 可以看作对顶点重新标号的过程。

我们枚举所有点编号的排列,此时我们只需要检查 \(G\)\(G'\) 是否同构即可。

如果他们同构,则说明这个排列是一种合法的变换。

接下来我们就可以求出变换的循环节个数,应用 Pólya 定理直接计算即可。

答案写的规范一点貌似是 \(\dfrac{1}{|G|} \sum\limits_{g \in G} m^{\sigma(g)}\)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long mod=10007;
bool G[20][20],new_G[20][20],vis[20];
int p[20];
long long inv(long long num){
	long long pre=mod-2,ans=1;
	while(pre){
		if(pre&1){
			ans=ans*num%mod;
		}
		num=num*num%mod;
		pre>>=1;
	}
	return ans;
}
int main(){
	int n,m,k;
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		G[u][v]=true;
		G[v][u]=true;
	}
	for(int i=1;i<=n;i++){
		p[i]=i;
	}
	long long ans=0,pret=0;
	do{
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				new_G[p[i]][p[j]]=G[i][j];
			}
		}
		bool flag=true;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(G[i][j]!=new_G[i][j]){
					flag=false;
				}
			}
		}
		if(flag){
			pret++;
			for(int i=1;i<=n;i++){
				vis[i]=false;
			}
			long long cum=1;
			for(int i=1;i<=n;i++){
				if(!vis[i]){
					cum*=k;
					cum%=mod;
					int pre=i;
					while(!vis[pre]){
						vis[pre]=true;
						pre=p[pre];
					}
				}
			}
			ans+=cum;
			ans%=mod;
		}
	}while(next_permutation(p+1,p+n+1));
	printf("%lld",ans*inv(pret)%mod);
	return 0;
}
posted @ 2025-12-26 19:07  Oken喵~  阅读(0)  评论(0)    收藏  举报