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;
}

浙公网安备 33010602011771号