考虑先不管染色,任意求出原图的一棵MST,设为 T T T。这个可以用经典的kruskal算法解决。
我们称生成树 T T T的权重和为 w ( T ) w(T) w(T)。
若 w ( T ) > X w(T)>X w(T)>X,显然一定无解。接下来我们假设 w ( T ) ≤ X w(T)\leq X w(T)≤X。
那么对于一个染色方案,若 T T T中同时有两种颜色的边,那它本身就是符合题意的生成树中权重和最小的之一。显然当且仅当 w ( T ) = X w(T)=X w(T)=X时,它对答案有 ( 2 n − 1 − 2 ) ⋅ 2 m − n + 1 (2^{n-1}-2)\cdot 2^{m-n+1} (2n−1−2)⋅2m−n+1的贡献( T T T中的边不能全部同色,不在 T T T中的边可以任意染色)。
接下来我们考虑 T T T中只有一种颜色的情况,不妨设 T T T中的边全部染成黑色,白色的情况贡献是相同的。
我们可以证明,这种情况下,若存在符合题意的生成树,那么一定有至少一棵是选择一条不在 T T T中的白色边 ( u , v ) (u,v) (u,v),替换掉 T T T上 u u u和 v v v间路径上的最大边权的边得到的。证明是我们可以考虑从 T T T开始,不断替换一条边为不在 T T T中的边,仍然得到一棵生成树。每次替换的过程都不会减小权重和,因此我们不会替换一条黑色边进去,并且也不会替换两条白色边。
进一步地,我们替换第 i i i条边的话,会增大 c o s t ( i ) = w i − max { w j ∣ ( u j , v j ) 在 T 中 ( u i , v i ) 两 点 间 路 径 上 } cost(i)=w_i-\max\{w_j|(u_j,v_j)在T中(u_i,v_i)两点间路径上\} cost(i)=wi−max{wj∣(uj,vj)在T中(ui,vi)两点间路径上}的贡献。因此我们一定会选择 c o s t cost cost最小的边替换。
有了上面的分析就可以计数了。我们把所有不在 T T T中的边按 c o s t cost cost排序( c o s t cost cost相同也强行定下顺序),那么第 i i i条边对答案有贡献当且仅当 w ( T ) + c o s t ( i ) = X w(T)+cost(i)=X w(T)+cost(i)=X,并且它是排序后第一条染成白色的边。假设它按 c o s t cost cost排序后排在 p o s pos pos的位置,会对答案有 2 m − n + 1 − p o s 2^{m-n+1-pos} 2m−n+1−pos的贡献(只有后面的边能任意染色)。
我们发现复杂度瓶颈在于计算 c o s t cost cost,即计算树上两点间的最大边权。因为范围不大,可以暴力枚举路径上的边查询,单次复杂度 O ( n ) \mathcal O(n) O(n),稍微用倍增优化一下即可单次复杂度 O ( log n ) \mathcal O(\log n) O(logn)。
时间复杂度视实现为 O ( n m ) \mathcal O(nm) O(nm)或 O ( m log n ) \mathcal O(m\log n) O(mlogn)。
#include <bits/stdc++.h>
#define FR first
#define SE second
#define MOD 1000000007
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
ll pow2[2005];
void pre(int n) {
pow2[0]=1;
for(int i=1;i<=n;i++) pow2[i]=pow2[i-1]*2LL%MOD;
}
vector <pr> ee[2005];
int fa[1005][15],maxn[1005][15];
int dep[1005];
void dfs(int x) {
for(int i=0;i<ee[x].size();i++)
if (ee[x][i].FR!=fa[x][0]) {
int u=ee[x][i].FR;
fa[u][0]=x;maxn[u][0]=ee[x][i].SE;
dep[u]=dep[x]+1;
for(int j=1;j<15;j++) {
fa[u][j]=fa[fa[u][j-1]][j-1];
maxn[u][j]=max(maxn[u][j-1],maxn[fa[u][j-1]][j-1]);
}
dfs(u);
}
}
int query(int x,int y) {
if (dep[x]<dep[y]) swap(x,y);
int ans=0,d=dep[x]-dep[y];
for(int i=0;i<15;i++)
if ((d>>i)&1) {
ans=max(ans,maxn[x][i]);
x=fa[x][i];
}
if (x==y) return ans;
for(int i=14;i>=0;i--)
if (fa[x][i]!=fa[y][i]) {
ans=max(ans,maxn[x][i]);
ans=max(ans,maxn[y][i]);
x=fa[x][i];
y=fa[y][i];
}
ans=max(ans,maxn[x][0]);
ans=max(ans,maxn[y][0]);
return ans;
}
namespace SETS {
int fa[1005];
void init(int n) {
for(int i=1;i<=n;i++) fa[i]=i;
}
int find_father(int x) {
return (fa[x]==x)?x:fa[x]=find_father(fa[x]);
}
bool check(int x,int y) {
x=find_father(x);y=find_father(y);
return x!=y;
}
void merge(int x,int y) {
x=find_father(x);y=find_father(y);
if (x==y) return;
fa[x]=y;
}
}
struct Edge {
int s,t,v;
Edge() {}
Edge(int a,int b,int c):s(a),t(b),v(c) {}
bool operator < (const Edge & b) const {return v<b.v;}
};
Edge e[2005];
bool in[2005];
int val[2005];
int main() {
int n,m;
ll X;
scanf("%d%d%lld",&n,&m,&X);
pre(m);
for(int i=1;i<=m;i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
e[i]=Edge(x,y,z);
}
sort(e+1,e+m+1);
SETS::init(n);
ll s=0;
for(int i=1;i<=m;i++)
if (SETS::check(e[i].s,e[i].t)) {
int u=e[i].s,v=e[i].t;
in[i]=1;
s+=e[i].v;
SETS::merge(u,v);
ee[u].push_back(pr(v,e[i].v));
ee[v].push_back(pr(u,e[i].v));
}
if (s>X) {
puts("0");
return 0;
}
int ans=0;
if (s==X) ans=(ans+(pow2[n-1]-2LL+MOD)*pow2[m-n+1])%MOD;
dfs(1);
int cnt=0;
for(int i=1;i<=m;i++)
if (!in[i]) val[++cnt]=e[i].v-query(e[i].s,e[i].t);
sort(val+1,val+cnt+1);
for(int i=1;i<=cnt;i++)
if (s+val[i]==X) ans=(ans+2LL*pow2[cnt-i])%MOD;
printf("%d\n",ans);
return 0;
}