[JSOI2008]最小生成树计数

Time Limit: 1 Sec    Memory Limit: 162 MB

Description

现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。

Input

第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。

Output

输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。

Sample Input

4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1


Sample Output

8
 
先做一次kruskal,求出每一种权值为Ci的边所用的条数(记作Ni)。根据定理,对于每一棵最小生成树,其中权值为Ci的边都必须用Ni条。用dfs枚举所用边,用并查集判断是否可行即可。最后用乘法原理求出答案。
 
AC CODE
 
program hy_1016;
const mo= 31011 ;
var x,y,c: array [ 1..1000 ] of longint ;
     fa,fa2,use: array [ 1..100 ] of longint ;
     p: array [ 1..1000 ] of boolean ;
     way,num,s,t,used,n,m: longint ;
//============================================================================
procedure qsort(l,r: longint );
var k,i,j,t: longint ;
begin
   k:=c[(l+r) shr 1 ]; i:=l; j:=r;
   repeat
     while c[i]<k do inc(i);
     while c[j]>k do dec(j);
     if i<=j then
     begin
       t:=c[i]; c[i]:=c[j]; c[j]:=t;
       t:=x[i]; x[i]:=x[j]; x[j]:=t;
       t:=y[i]; y[i]:=y[j]; y[j]:=t;
       inc(i); dec(j);
     end ;
   until i>j;
   if j>l then qsort(l,j);
   if i<r then qsort(i,r);
end ;
//============================================================================
procedure init;
var i: longint ;
begin
   readln(n,m);
   for i:= 1 to m do readln(x[i],y[i],c[i]);
   for i:= 1 to n do fa[i]:=i;
   for i:= 1 to m do p[i]:= false ;
   qsort( 1 ,m);
end ;
//============================================================================
function getfa(x: longint ): longint ;
begin
   if fa[x]<>x then fa[x]:=getfa(fa[x]);
   exit(fa[x]);
end ;
//============================================================================
function getfa2(x: longint ): longint ;
begin
   if fa2[x]<>x then fa2[x]:=getfa2(fa2[x]);
   exit(fa2[x]);
end ;
//============================================================================
procedure kruskal;
var all,i,fx,fy: longint ;
begin
   all:= 0 ;
   for i:= 1 to m do
   begin
     fx:=getfa(x[i]);
     fy:=getfa(y[i]);
     if fx=fy then continue
     else fa[fx]:=fy;
     inc(all); p[i]:= true ;
     if all=n- 1 then break;
   end ;
   if all<n- 1 then
   begin writeln ( '0' ); halt; end ;
end ;
//============================================================================
procedure judge;
var i,fx,fy: longint ;
begin
   for i:= 1 to n do fa2[i]:=fa[i];
   for i:=s to t do
     if use[i-s+ 1 ]= 1 then
     begin
       fx:=getfa2(x[i]);
       fy:=getfa2(y[i]);
       fa2[fx]:=fy;
       if fx=fy then exit;
     end ; inc(way);
end ;
//============================================================================
procedure dfs(x: longint );
begin
   if x=t-s+ 2 then begin judge; exit; end ;
   if used+t-s+ 1 -x+ 1 =num then
   begin
     use[x]:= 1 ; inc(used);
     dfs(x+ 1 ); dec(used);
   end else
   begin
     use[x]:= 0 ; dfs(x+ 1 );
     use[x]:= 1 ;
     inc(used); dfs(x+ 1 ); dec(used);
   end ;
end ;
//============================================================================
procedure work;
var i,fx,fy,ans: longint ;
begin
   s:= 0 ; t:= 0 ; ans:= 1 ;
   while t<m do
   begin s:=t+ 1 ; num:= 0 ;
     for i:=s to m do
     begin
       if p[i] then inc(num);
       if c[i]<>c[i+ 1 ] then break;
     end ; t:=i;
     if num= 0 then continue;
     for i:= 1 to n do fa[i]:=i;
     for i:= 1 to s- 1 do
       if p[i] then
       begin
         fx:=getfa(x[i]);
         fy:=getfa(y[i]);
         fa[fx]:=fy;
       end ;
     for i:=t+ 1 to m do
       if p[i] then
       begin
         fx:=getfa(x[i]);
         fy:=getfa(y[i]);
         fa[fx]:=fy;
       end ;
     used:= 0 ; way:= 0 ; dfs( 1 );
     ans:=(ans*way) mod mo;
   end ; writeln (ans);
end ;
//============================================================================
begin
   init;
   kruskal;
   work;
end .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值