[JSOI2008]最小生成树计数

本文介绍了一种算法,用于计算给定加权无向图中不同最小生成树的数量,并通过Kruskal算法和深度优先搜索结合并查集来实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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、付费专栏及课程。

余额充值