250218模拟赛 T2 color 题解 | 广义串并联

题意

给定一个 \(n\) 个点 \(m\) 条边的联通无向图,给图上每个点染上 \(k\) 种颜色中的一种,且要
求每一条边的两个端点不同色(不需要使用全部 \(k\) 种颜色),求方案数 \(\bmod 10^9+7\)

\(n\le 10^5\)
\(m\le n+5\)

题解

按照题解的说法,图的染色问题在多项式时间内不可解。注意到题目给定了一额外限制条件:\(m\le n+5\)。对于这种形式的约束条件,我们可以用广义串并联图的缩边方法,缩掉 \(1\) 度点和 \(2\) 度点。然而广义串并联图最终可以缩成一个点,这个 \(m\le n+5\) 的图会缩成什么呢?考虑三类缩边操作:

  • \(1\) 度点:--m, --n
  • \(2\) 度点:--m, --n
  • 叠合重边:--m

因此最终的边数 \(m'\) 仍然不超过 \(n'+5\);同时最终的图上不包含度数小于 \(3\) 的节点(都被删掉了),因此有 \(3n\le 2m\),联立解得 \(n\le 10,m\le 15\),可以直接暴力深搜求解。

三类操作会改变边的属性。但是原本的边的限制是:两端点颜色不能相等;然而缩边之后两端点颜色就可以相等了,并且也会对答案产生影响。因此我们给每条边赋予两个权值:\(w_1,w_2\),分别表示两端点颜色相等、不等,边内部染色的方案数(容易注意到边内部染色方案和两端点具体的颜色无关,只与其相等关系有关)。缩 \(2\) 度点时考察中间点和两端点的颜色关系,缩 \(1\) 度点时将 \(w_1+w_2\) 乘到答案的系数上,叠合重边时直接将 \(w_1,w_2\) 对应相加即可。最终得到一个 \(n\le 10\) 的图,在上面暴力枚举各个节点之间的颜色的相等关系即可。

代码
#include<iostream>
#include<set>
#include<queue>
#define int long long
using namespace std;
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;

struct Edge {
    int v, w1, w2;
    inline bool operator<(const Edge &other) const {
        return v < other.v;
    }
};

struct Pt {
    int u, deg;
    inline bool operator<(const Pt &other) const {
        return deg < other.deg;
    }
    inline bool operator>(const Pt &other) const {
        return deg > other.deg;
    }
};

int n, m, k;
int deg[N], vis[N];
set<Edge> E[N];
priority_queue<Pt, vector<Pt>, greater<Pt> > V;

int ans, mul = 1;

int valid[12], n2;
int col[N];

void dfs(int x, int ccnt, int pans) {
    if(!pans) return;
    if(x == n2 + 1) {
        for(int i = k; i >= k - ccnt + 1; i--) {
            pans = pans * i % MOD;
        }
        ans = (ans + pans) % MOD;
        return;
    }
    for(int i = 1; i <= ccnt + 1; i++) {
        int tmp = 1;
        for(auto j = E[valid[x]].begin(); j != E[valid[x]].end(); ++j) {
            if(j->v > valid[x]) break;
            tmp = (tmp * ((i == col[j->v]) ? j->w1 : j->w2)) % MOD;
        }
        col[valid[x]] = i;
        dfs(x + 1, max(i, ccnt), pans * tmp % MOD);
    }
}

// 加边的同时去除重边
void addEdge(int u, int v, int w1, int w2) {
    auto it = E[u].find({v, 0, 0});
    if(it != E[u].end()) {
        int ww1 = it->w1, ww2 = it->w2;
        E[u].erase({v, 0, 0});
        E[v].erase({u, 0, 0});
        E[u].insert({v, w1 * ww1 % MOD, w2 * ww2 % MOD});
        E[v].insert({u, w1 * ww1 % MOD, w2 * ww2 % MOD});
    } else {
        E[u].insert({v, w1, w2});
        E[v].insert({u, w1, w2});
    }
}

signed main() {

    freopen("color.in", "r", stdin);
    freopen("color.out", "w", stdout);

    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m >> k;
    for(int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        E[u].insert({v, 0, 1});
        E[v].insert({u, 0, 1});
    }
    for(int i = 1; i <= n; i++) {
        deg[i] = E[i].size();
        V.push({i, deg[i]});
    }

    while(!V.empty() && V.top().deg <= 2) {
        int u = V.top().u;
        if(vis[u] || V.top().deg != deg[u]) {
            V.pop();
            continue;
        }
        V.pop();
        if(deg[u] == 0) {
            vis[u] = 1;
            mul = mul * k % MOD;
            continue;
        } else if(deg[u] == 1) {
            Edge e = *E[u].begin();
            // cout << u << ' ' << e.v << ' ' << e.w1 << ' ' << e.w2 << endl;
            mul = mul * (e.w1 + e.w2 * (k - 1) % MOD) % MOD;
            vis[u] = 1;
            E[e.v].erase({u, 0, 0});
            deg[e.v]--;
            V.push({e.v, deg[e.v]});
        } else {
            Edge e1 = *E[u].begin();
            Edge e2 = *E[u].rbegin();
            int a = e1.v, b = e2.v;
            int w1 = (e1.w1 * e2.w1 % MOD + e1.w2 * e2.w2 % MOD * (k - 1) % MOD) % MOD;
            int w2 = (e1.w1 * e2.w2 % MOD + e1.w2 * e2.w1 % MOD + e1.w2 * e2.w2 % MOD * (k - 2) % MOD) % MOD;
            vis[u] = 1;
            E[a].erase({u, 0, 0});
            E[b].erase({u, 0, 0});
            addEdge(a, b, w1, w2);
            if(E[a].size() != deg[a]) {
                deg[a] = E[a].size();
                V.push({a, deg[a]});
            }
            if(E[b].size() != deg[b]) {
                deg[b] = E[b].size();
                V.push({b, deg[b]});
            }
        }
    }

    for(int i = 1; i <= n; i++) {
        if(!vis[i]) valid[++n2] = i;
    }

    // cout << "n2: " << n2 << endl;
    if(n2 > 10) throw -1;

    dfs(1, 0, 1);

    cout << ans * mul % MOD << endl;

    return 0;
}
posted @ 2025-02-25 16:46  Simon火腿肠  阅读(55)  评论(2)    收藏  举报