洛谷 P8633 [蓝桥杯 2015 国 B] 模型染色 题解

题面分析

\(n\) 个点,用 \(k\) 个颜色进行染色,有 \(m\) 条边,求非等价着色数。

关于主要内容

对于这样一道考 Pólya 定理的题目,我考虑直接套用式子

\[N(G,\mathcal{C}) = \frac{1}{|G|} \sum_{f \in G}{\#(f)} \]

其中 \(N(G,\mathcal{C})\) 为非等价着色数,\(G\) 为置换群,\(f\) 为一个置换,\(\#(f)\) 表示置换的循环因子分解中循环的个数。

\(f\)\(X\) 的一个置换,\(D_f = (X,A_f)\) 是顶点集为 \(X\) 且弧集为 \(A_f=\{(i,f(i)):i \in X\}\) 的有向图。\(A_f\) 可以划分成若干有向圈,且每个顶点恰好只属于一个有向圈。

如果在一个置换中某些元素以循环的方式置换且余下的元素保持不变,则称其为循环置换,简称循环。我们称有 \(k\) 个元素的循环为 \(k\) 循环。

于是对应于合成运算,\(f\) 有唯一的循环因子分解。

\[f = [i_1,i_2, \cdots,i_p] \circ [j_1,j_2, \cdots,j_p] \circ \cdots \circ [l_1,l_2, \cdots,l_p] \]

记置换 \(f\) 的循环分解中的循环个数为 \(\#(f)\)

举个例子,

置换

\[f = \begin{pmatrix} 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8\\ 4 & 3 & 2 & 6 & 1 & 5 & 7 & 8\\ \end{pmatrix}\\ = [1,4,5,6] \circ [2,3] \circ [7] \circ [8] \]

其中 \(\#(f)=4\)

关于限制

对于每条边 \((u,v)\) 我们要保证在置换

\[f = \begin{pmatrix} 1 & 2 & \cdots & n\\ i_1 & i_2 & \cdots & i_n\\ \end{pmatrix} \]

作用后仍存在边 \((i_u,i_v)\)

关于数据范围

注意到 \(n\) 很小,所以我们可以直接枚举 \(n^2\) 种置换,再判断每种置换是否符合限制条件,而且可以用领接矩阵存边。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long

int rd()//快读
{
    int x = 0,w = 1;
    char ch = 0;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = x * 10 + (ch - '0');
        ch = getchar();
    }
    return x * w;
}

const int N = 15;
const int mod = 10007;

int Pow(int n,int k)//快速幂
{
    int ans = 1;
    while(k)
    {
        if(k & 1)
        {
            ans *= n;
            ans %= mod;
        }
        n *= n;
        n %= mod;
        k >>= 1;
    }
    return ans;
}

int n,m,k;
bool mp[N][N],vis[N];
int a[N],sz[N],tot;

bool check()
{
    for(int i = 1;i <= n;i++)
        for(int j = i;j <= n;j++)
            if(mp[i][j] && !mp[a[i]][a[j]]) return 0;
    return 1;
}

void get()//记录循环因子个数
{
    memset(vis,0,sizeof(vis));
    tot = 0;
    for(int i = 1;i <= n;i++)
    {
        if(!vis[i])
        {
            int pos = i;
            while(!vis[pos])
            {
                vis[pos] = 1;
                pos = a[pos];
            }
            tot++;
        }
    }
}

signed main()
{
    n = rd(),m = rd(),k = rd();
    while(m--)
    {
        int u = rd(),v = rd();
        mp[u][v] = mp[v][u] = 1;
    }
    for(int i = 1;i <= n;i++) a[i] = i;
    int ans = 0,sum = 0;
    while(1)
    {
        if(check())
        {
            sum++;
            get();
            ans = (ans + Pow(k,tot)) % mod;
        }
        if(!next_permutation(a + 1,a + n + 1)) break;//全排列函数
    }
    cout << ans * Pow(sum,mod - 2) % mod;
    return 0;
}

样例解释似乎错了

posted @ 2025-05-07 19:56  IC0CI  阅读(8)  评论(0)    收藏  举报