题意:求n个点、m条边的不同的最小生成树的方案数
每种边权的边数量固定、作用固定
先做一遍最小生成树,求出每种边权在最小生成树中的数量num[i]
再从小到大对每种边权进行dfs,求出对于第i种边权,有多少种满足num[i]的取法
根据乘法原理乘上即可
对于已经处理完的第i种边权,把该种边权所有的边能加到最小生成树的就加进去,再进行下一种边权的判断
注意并查集不要用路径压缩,不然不方便分开联通块(不容易还原)
const
mo=31011;
type
rec=record
x,y,len:longint;
end;
type
rec1=record
l,r,num:longint;
end;
var
n,m,tt,tot,tx,ty:longint;
sum,ans :longint;
i,j :longint;
l :array[0..1010] of rec;
a :array[0..1010] of rec1;
father :array[0..110] of longint;
function get_father(x:longint):longint;
begin
if x=father[x] then exit(x);
exit(get_father(father[x]));
end;
procedure sort(ll,rr:longint);
var
i,j,x:longint;
y:rec;
begin
i:=ll; j:=rr; x:=l[(ll+rr)>>1].len;
while i<=j do
begin
while l[i].len<x do inc(i);
while l[j].len>x do dec(j);
if i<=j then
begin
y:=l[i]; l[i]:=l[j]; l[j]:=y;
inc(i); dec(j);
end;
end;
if i<rr then sort(i,rr);
if j>ll then sort(ll,j);
end;
procedure dfs(x,now,num:longint);
var
tx,ty:longint;
begin
if now=a[x].r+1 then
begin
if num=a[x].num then inc(sum); exit;
end;
tx:=get_father(l[now].x);
ty:=get_father(l[now].y);
if tx<>ty then
begin
father[tx]:=ty;
dfs(x,now+1,num+1);
father[tx]:=tx;
end;
dfs(x,now+1,num);
end;
begin
read(n,m);
for i:=1 to m do read(l[i].x,l[i].y,l[i].len);
sort(1,m);
tt:=0; tot:=0;
for i:=1 to n do father[i]:=i;
for i:=1 to m do
begin
if l[i].len<>l[i-1].len then
begin
inc(tot); a[tot].l:=i; a[tot-1].r:=i-1;
end;
tx:=get_father(l[i].x);
ty:=get_father(l[i].y);
if tx<>ty then
begin
father[tx]:=ty;
inc(tt);
inc(a[tot].num);
end;
end;
a[tot].r:=m;
if tt<>n-1 then
begin
writeln(0); exit;
end;
//
ans:=1;
for i:=1 to n do father[i]:=i;
for i:=1 to tot do
begin
sum:=0;
dfs(i,a[i].l,0);
ans:=(ans*sum) mod mo;
for j:=a[i].l to a[i].r do
begin
tx:=get_father(l[j].x);
ty:=get_father(l[j].y);
if tx<>ty then father[tx]:=ty;
end;
end;
writeln(ans);
end.
——by Eirlys