题意:
给出一个 N N 个点条边的无向图,现在要给一些边染黑色或者白色,现在从这些边中选出一些边生成若干棵生成树,要求这些生成树至少包含一条白色的边和一条黑色的边,而且这些生成树的最小值是 X X ,问有多少种染色方法。
思路:
首先对原图求一下最小生成树,计算出最小生成树的权重 w w ,那么有以下三种情况:
- :这种情况下显然答案是
0
0
- w=X w = X :对于这种情况,那么选出的黑色或者白色的边必然是原图最小生成树边的集合,这个只要找出所有的属于原图最小生成树的边,假设原图最小生成树中的所有边形成集合 S S ,总共有条边,那么可以知道,不属于 S S 的边可以任意染色,属于的边只要除去全部染成黑色和全部染成白色的就可以,答案就是 2M−m∗(2m−1) 2 M − m ∗ ( 2 m − 1 ) 种
-
w<X
w
<
X
:首先必须把
S
S
里面的的所有边涂成黑色(或者白色),先假设全部涂成黑色,那么剩余的边,对每一条边单独处理,对边,它染成白色添加进去之后,如果和集合
S
S
中的边形成的满足题意的最小生成树权重小于
X
X
,那么这条边只能继续涂成黑色;如果大于
X
X
,它涂成黑色白色都无所谓;然后把等于
X
X
的边的数量统计下来,假设有条边,可以知道这
tot
t
o
t
条边只要有一条边是白色就行了,因为只要选中这里面的一条边,剩余的边肯定都是从集合
S
S
中选出来了, 不可能再从其它边中选取,如果必须涂成黑色的有条边, 那么答案就是
2M−tot−d∗(2tot−1)
2
M
−
t
o
t
−
d
∗
(
2
t
o
t
−
1
)
,最终结果再乘以
2
2
就行了,就是必须涂成一种颜色的有两种涂法
#include<bits/stdc++.h> typedef long long ll; const ll maxn = 1e3 + 10; const ll mod = 1e9 + 7; using namespace std; struct P { ll from, to, w; P() {} P(ll f, ll t, ll w) : from(f), to(t), w(w) {} bool operator < (P p) const { return w < p.w; } }; ll n, m, pre[maxn], is[maxn * 2]; vector<P> edge, G[maxn]; ll anc[maxn][12], deep[maxn]; ll cost[maxn][12], c[maxn], x; ll findset(ll x) { return x == pre[x] ? x : pre[x] = findset(pre[x]); } void dfs(ll x, ll fa, ll d, ll ws) { deep[x] = d; pre[x] = fa; c[x] = ws; for(ll i = 0; i < G[x].size(); i++) { P e = G[x][i]; ll to = e.to, w = e.w; if(to == fa) continue; dfs(to, x, d + 1, w); } } void preprocess() { memset(anc, -1, sizeof anc); memset(cost, -1, sizeof cost); for(ll i = 1; i <= n; i++) { cost[i][0] = c[i]; anc[i][0] = pre[i]; } for(ll j = 1; (1 << j) <= n; j++) { for(ll i = 1; i <= n; i++) { ll t = anc[i][j - 1]; if(t == -1) continue; cost[i][j] = max(cost[i][j - 1], cost[t][j - 1]); anc[i][j] = anc[t][j - 1]; } } } ll query(ll x, ll y) { if(deep[x] > deep[y]) swap(x, y); ll lg = 0; while((1 << lg) <= deep[y]) lg++; lg--; ll ans = 0; for(ll i = lg; i >= 0; i--) { if(deep[y] - deep[x] >= (1 << i)) { ans = max(ans, cost[y][i]); y = anc[y][i]; } } if(x == y) return ans; for(ll i = lg; i >= 0; i--) { if(anc[x][i] == anc[y][i]) continue; ans = max(ans, cost[x][i]); ans = max(ans, cost[y][i]); x = anc[x][i]; y = anc[y][i]; } ans = max(ans, max(c[x], c[y])); return ans; } ll qmod(ll x, ll n, ll mod) { ll ans = 1; while(n) { if(n & 1) ans = ans * x % mod; x = x * x % mod; n >>= 1; } return ans; } int main() { while(scanf("%lld %lld", &n, &m) != EOF) { scanf("%lld", &x); memset(is, 0, sizeof is); for(ll i = 0; i < maxn; i++) { G[i].clear(); pre[i] = i; } edge.clear(); for(ll i = 0; i < m; i++) { ll u, v, w; scanf("%lld %lld %lld", &u, &v, &w); edge.push_back(P(u, v, w)); } sort(edge.begin(), edge.end()); ll tot_wight = 0, ed = 0; for(ll i = 0; i < edge.size(); i++) { P e = edge[i]; ll u = e.from, v = e.to, w = e.w; ll nu = findset(u), nv = findset(v); if(nu == nv) continue; is[i] = 1; ed++; tot_wight += w; pre[nu] = nv; G[u].push_back(P(u, v, w)); G[v].push_back(P(v, u, w)); } dfs(1, 0, 1, 0); preprocess(); for(ll i = 0; i < edge.size(); i++) { if(is[i]) continue; ll from = edge[i].from, to = edge[i].to; ll max_w = query(from, to); if(max_w == edge[i].w) { is[i] = 1; ed++; } } ll ans; if(tot_wight > x) ans = 0; else if(tot_wight == x) { ans = qmod(2, m - ed, mod); ans = ans * (qmod(2, ed, mod) - 2) % mod; if(ans < 0) ans = ans + mod; } else { ll dx = 0; for(ll i = 0; i < edge.size(); i++) { if(is[i]) continue; ll max_w = query(edge[i].from, edge[i].to); if(tot_wight - max_w + edge[i].w == x) dx++; else if(tot_wight - max_w + edge[i].w < x) ed++; } ans = qmod(2, m - ed - dx, mod); ll col = 2 * (qmod(2, dx, mod) - 1) % mod; ans = ans * col % mod; if(ans < 0) ans = ans + mod; } cout << ans << endl; } return 0; }