我的天,改一个晚上才改出来,一个及其细微难以发现的逻辑错误:在做最小生成树顺便保留并查集状态的时候,不论每次是否处理的是新的种类的边(离散化后的),都应当先将fa拷贝到nowfa当中去,否则将会出现一种边没有被使用过(因为加入后会形成环),从而其相对应的nowfa均为0,导致在dfs判断所选则的边会不会形成环时得出有环的结论(因为fa[a]、fa[b]都是0),进而引起方案数出现0的情况。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
using namespace std;
typedef long long ll;
const int inf=1e9+10,mod=31011,N=666,M=2222;
const double eps=1e-6;
struct edge{
int a,b,w;
bool friend operator < (edge a,edge b){
return a.w<b.w;
}
}e[M];
int n,m,cnt[M],fa[N];//cnt[i]表示权值排第i的边在最小生成树用到了几条。
int nowfa[M][N],st[N],ret;
ll ans=1;
int getf(int v){ return fa[v]==v?v:fa[v]=getf(fa[v]);}
bool selected[M];
int tmpfa[N];
int tmpgetf(int v) { return tmpfa[v]==v?v:tmpfa[v]=tmpgetf(tmpfa[v]);}
void dfs(int v,int pos,int sel){
if(sel==cnt[v]){
memcpy(tmpfa,nowfa[v-1],sizeof(nowfa[v-1]));
rep(i,st[v],pos) if(selected[i]){
int faa=tmpgetf(e[i].a),fab=tmpgetf(e[i].b);
if(faa==fab) return;
tmpfa[fab]=faa;
}
ret++;
return;
}
if(pos>=st[v+1])return;
dfs(v,pos+1,sel);
selected[pos]=true;
dfs(v,pos+1,sel+1);
selected[pos]=false;
}
void process(int v){
ret=0;
if(cnt[v]==0) return;
dfs(v,st[v],0);
ans=(ans*(ll)ret)%mod;
return;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
cin>>n>>m;
rep(i,1,m) cin>>e[i].a>>e[i].b>>e[i].w;
sort(e+1,e+1+m);
int now=0,num=0;
rep(i,1,m){//离散化处理
if(e[i].w!=now){
now=e[i].w;
e[i].w=++num;
st[e[i].w]=i;
}else e[i].w=num;
}
st[e[m].w+1]=m+1;
rep(i,1,n) fa[i]=i;
int last=0,tot=0;
rep(i,1,m){
int faa=getf(e[i].a),fab=getf(e[i].b);
memcpy(nowfa[last],fa,sizeof(fa));
last=e[i].w;
if(faa==fab) continue;
cnt[e[i].w]++; fa[fab]=faa; tot++;
}
if(tot<n-1){
cout<<0;
return 0;
}
rep(i,1,num) process(i);//处理权值为i的边
cout<<ans;
return 0;
}