[蓝桥杯 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;
}

浙公网安备 33010602011771号