T3AT3980 [ARC093C] Bichrome Spanning Tree
在一张图上黑白染色,使得同时包含有黑边和白边的最小生成树权值恰好为X。问有多少种染色方法?
(n <= 1000 , m <= 2000)
先求出不管黑白边的MST,然后记 det 为X与之的差值。
之后对于每条非MST边 , 求出它的权值-树上的边的最大值 , 记这个值为val。
那么对于val分类,$ < det$ 的记为cnt0 , (= det) 记为cnt1 , (> det) 记为cnt2。
然后分类讨论。
若(del < 0) 肯定不合法。
若(del = 0)
有两种情况,一种是最开始的MST就合法,这种方案数是((2 ^ {n - 1} - 2) * 2 ^ {m - n + 1}) 解释一下,就是枚举树上边的染色方案,但是有全黑和全白不能要,再给它乘上非树边的贡献。
还有一种是,最开始的那个不合法,需要给他换边,方案数是(2 * (2 ^ {cnt1} - 1) * 2 ^ {cnt2}) 解释一下就是先枚举最开始的MST是全黑还是全白,然后要保证 cnt1 条 val = det 的边至少存在一条能被替换, 对于cnt2的边是不会被用到的所以可以随便,对于cnt0的边不能选,要不然权值会更小,但这显然不合法。
若(det = 1)
那么一开始的MST肯定不能选,它的方案就是(del = 0)的第二种情况。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<string>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1010 , mod = 1e9+7;
#define int long long
inline int read()
{
register int x = 0 , f = 0; register char c = getchar();
while(c < '0' || c > '9') f |= c == '-' , c = getchar();
while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
return f ? -x : x;
}
int n , m , cnt , del , goal , mx;
int fa[N] , head[N] , used[N << 1];
struct node{ int u , v , w; bool operator < (const node &A) const { return w < A.w; }; }E[N << 1];
struct edge{ int v , nex , c; }e[N << 1];
inline void add(int u , int v , int c) { e[++cnt].v = v; e[cnt].nex = head[u]; e[cnt].c = c; head[u] = cnt; }
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
inline int Union(int x , int y)
{
x = find(x); y = find(y); if(x == y) return 0;
fa[x] = y; return 1;
}
void dfs(int x , int fa , int m)
{
if(x == goal) { mx = m; return ; }
for(int i = head[x] ; i && mx == -1 ; i = e[i].nex) if(e[i].v != fa) dfs(e[i].v , x , max(m , e[i].c));
}
int Query(int u , int v)
{
goal = v; mx = -1; dfs(u , 0 , 0); return mx;
}
inline int ksm(int a , int k) { a %= mod; int ans = 1; for( ; k ; k >>= 1 , a = a * a % mod) if(k & 1) ans = ans * a % mod; return ans; }
signed main()
{
n = read(); m = read(); del = read();
for(int i = 1 ; i <= n ; ++i) fa[i] = i;
for(int i = 1 ; i <= m ; ++i) E[i].u = read() , E[i].v = read() , E[i].w = read();
sort(E + 1 , E + 1 + m);
for(int i = 1 ; i <= m ; ++i) if(Union(E[i].u , E[i].v)) add(E[i].u , E[i].v , E[i].w) , add(E[i].v , E[i].u , E[i].w) , del -= E[i].w , used[i] = 1;
if(del < 0) { puts("0"); return 0; }
int cnt0 = 0 , cnt1 = 0 , cnt2 = 0 , val;
for(int i = 1 ; i <= m ; ++i) if(!used[i])
{
val = E[i].w - Query(E[i].u , E[i].v);
if(val < del) cnt0++; else if(val == del) cnt1++; else cnt2++;
}
int ans = 2LL * (ksm(2 , cnt1) - 1) % mod * ksm(2 , cnt2) % mod;
if(del == 0) ans = (ans + (ksm(2 , n - 1) - 2) * ksm(2 , m - n + 1) % mod) % mod;
ans = (ans % mod + mod) % mod; cout << ans << '
';
return 0;
}