简要题意:
给一张连通无向图,每条边有边权。
你需要把边分成两个集合。
定义一个合法的生成树为分别包含两个集合中至少一条边的树。
请问你有多少种划分方式,使得最小的合法生成树边权和为 X X X
题解:
首先手玩+感性理解+理性证明可以知道满足条件的最小生成树与真实最小生成树最多相差一条边。
设真实最小生成树边权之和为 s u m sum sum。
当 s u m > X sum>X sum>X,显然无解。
否则我们考虑枚举这条强行加上的边,显然扣掉的就是路径最大值,这个可以树上倍增求一下。
当 s u m = X sum=X sum=X,我们求出有多少条非树边强行加上之后权值不变,那么无法达到目标权值当且仅当所有树边和这些非树边在同一集合里面,用总方案减去即可。
当 s u m < X sum<X sum<X,我们求出有多少条非树边强行加上之后达到目标,有多少条非树边强行加上之后比目标小,分别设为 o k ok ok和 b a n ban ban,容易注意到这时候必须强行令MST和 b a n ban ban中的边在同一个集合中, o k ok ok条边至少一条要在另一个集合中。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
inline ll gl(){return get<ll>();}
}
using namespace IO;
using std::cerr;
using std::cout;
cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int po(int a,int b){
int r=1;for(;b;b>>=1,a=mul(a,a))
if(b&1)r=mul(r,a);return r;
}
cs int N=1e5+7,M=N<<1|1;
int n,m;
int bel[N];
int gf(int u){
while(bel[u]>0&&bel[bel[u]]>0)u=bel[u]=bel[bel[u]];
return bel[u]>0?bel[u]:u;
}
bool mg(int u,int v){
while(bel[u]>0&&bel[bel[u]]>0)u=bel[u]=bel[bel[u]];bel[u]>0&&(u=bel[u]);
while(bel[v]>0&&bel[bel[v]]>0)v=bel[v]=bel[bel[v]];bel[v]>0&&(v=bel[v]);
if(u==v)return false;bel[u]<bel[v]?(--bel[u],bel[v]=u):(--bel[v],bel[u]=v);
return true;
}
int Log[N];
int el[N],nxt[M],to[M],w[M],ec;
inline void adde(int u,int v,int vl){
nxt[++ec]=el[u],el[u]=ec,to[ec]=v,w[ec]=vl;
nxt[++ec]=el[v],el[v]=ec,to[ec]=u,w[ec]=vl;
}
int d[N];
int f[19][N],mx[19][N];
void dfs(int u,int p,int pre_w){
d[u]=d[p]+1;f[0][u]=p;mx[0][u]=pre_w;
for(int re i=0;f[i][u];++i){
f[i+1][u]=f[i][f[i][u]];
mx[i+1][u]=std::max(mx[i][f[i][u]],mx[i][u]);
}
for(int re e=el[u];e;e=nxt[e])
if(to[e]!=p)dfs(to[e],u,w[e]);
}
int qmx(int u,int v){int ans=0;
if(d[u]<d[v])std::swap(u,v);
for(int re i=Log[d[u]-d[v]];~i;--i){
if((1<<i)<=(d[u]-d[v])){ans=std::max(ans,mx[i][u]);u=f[i][u];}
}if(u==v)return ans;
for(int re i=Log[d[u]];~i;--i){
if(f[i][u]!=f[i][v]){
ans=std::max(ans,mx[i][u]);
ans=std::max(ans,mx[i][v]);
u=f[i][u],v=f[i][v];
}
}return std::max(ans,std::max(mx[0][u],mx[0][v]));
}
int on_mst[M];
struct edge{int u,v,w;}e[M];
void solve(){
n=gi(),m=gi();ec=0;
ll sum=0,X=gl();
memset(el,0,sizeof el);
memset(bel,0,sizeof bel);
memset(f,0,sizeof f);
memset(mx,0,sizeof mx);
for(int re i=1;i<=m;++i){
int u=gi(),v=gi(),w=gi();
e[i]=(edge){u,v,w};
}std::sort(e+1,e+m+1,[](cs edge &a,cs edge &b){return a.w<b.w;});
for(int re i=1;i<=m;++i){
if(on_mst[i]=mg(e[i].u,e[i].v))
adde(e[i].u,e[i].v,e[i].w),sum+=e[i].w;
}dfs(1,0,0);
if(sum>X)cout<<"0\n";
else if(sum==X){int ok=0;
for(int re i=1;i<=m;++i)if(!on_mst[i]){
if(qmx(e[i].u,e[i].v)==e[i].w)++ok;
}
cout<<dec(po(2,m),po(2,m-n-ok+2))<<"\n";
}else {int ok=0,ban=0;
for(int re i=1;i<=m;++i)if(!on_mst[i]){
ll now=sum-qmx(e[i].u,e[i].v)+e[i].w;
if(now==X)++ok;else if(now<X)++ban;
}
cout<<dec(po(2,m-n-ban+2),po(2,m-n-ban-ok+2))<<"\n";
}
}
signed main(){
#ifdef zxyoi
freopen("zhuzhu.in","r",stdin);
#endif
Log[0]=-1;for(int re i=2;i<N;++i)Log[i]=Log[i>>1]+1;
int T=gi();while(T--)solve();
return 0;
}