emmm数据太水,直接爆搜
有一个定理:无论最小生成树由哪些边组成,其中使用的每种边权的边的数量是一定的
所以由此我们可以得到一种做法:记录最小生成树使用的每种边的数量,然后dfs使用这些边去构建另一棵最小生成树
代码
//By AcerMo
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=2050;
const int mod=31011;
struct edge{int from,to,cost;}emm[M];//存原图构树
bool cmp(edge a,edge b){return a.cost<b.cost;}
struct nmap{int from,to,num;}map[M];//记录最小生成树种每种边权的数量
int n,m,cnt=0,all=0;
int fa[M],siz[M];
int find(int x)
{
if (x!=fa[x]) return find(fa[x]);
return x;
}
void dfs(int x,int now,int tot)
{
if (now==map[x].to+1)
{
if (tot==map[x].num) all++;
return ;
}//无论树如何,在保证最小生成树的情况下,每种边权的边的数量不变
int r1=find(emm[now].from);
int r2=find(emm[now].to);
if (r1!=r2)
{
fa[r1]=r2;
dfs(x,now+1,tot+1);
fa[r1]=r1;fa[r2]=r2;
}//合并以当前边权的边为连接的的联通块
dfs(x,now+1,tot);
return ;
}
void kthtree()
{
for (int i=1;i<=n;i++) fa[i]=i;int ans=1;
for (int i=1;i<=cnt;i++)
{
dfs(i,map[i].from,0);
ans=(ans*all)%mod;
all=0;
for (int k=map[i].from;k<=map[i].to;k++)
{
int r1=find(emm[k].from);
int r2=find(emm[k].to);
if (r1!=r2) fa[r1]=r2;
}
}//乘法原理
cout<<ans;
return ;
}
void kru()
{
for (int i=1;i<=n;i++) fa[i]=i;
int tot=1,sum=1;
for (int i=1;i<=m;i++)
{
if (emm[i].cost!=emm[i-1].cost){map[++cnt].from=i;map[cnt-1].to=i-1;}
//记录每种边权的边在边权数组中的区间
int r1=find(emm[i].from);int r2=find(emm[i].to);
if (r1!=r2) fa[r1]=r2,map[cnt].num++,tot++;//记录熟练
}//最小生成树
if (tot<n){puts("0");return ;}
map[cnt].to=m;kthtree();
return ;
}
int main()
{
scanf("%d%d",&n,&m);int a,b,c;
for (int i=1;i<=m;i++)
scanf("%d%d%d",&emm[i].from,&emm[i].to,&emm[i].cost);
sort(emm+1,emm+m+1,cmp);kru();
return 0;
}