JSOI2008 最小生成树计数
JSOI2008 最小生成树计数
【题目描述】
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。
【输入文件】
第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。
【输出文件】
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
【输入样例】
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
【输出样例】
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
【输出样例】
8
【题目分析】
一开始超时了,加了半天优化发现快排写错了……
继续……水题……首先按kruskal的思路做一遍最小生成树,统计一下各个相同权值的边分别加进去了几条,然后对每组相同权值的边进行dfs,计算对于每组边有多少种加入先前计算出的边的数量的方案,乘在一起即可。注意,dfs时并查集朴素合并,不要进行路径压缩,否则无法进行状态还原,处理完每组边后,修改连通性时再进行状态压缩。再次注意,有不连通的情况,此时输出0。
【代码实现】
Code
1 program tyvj1826;
2 var s,t,l:array[0..1001]of longint;
3 f:array[0..1000]of longint;
4 g,ss:array[0..1000]of longint;
5 i,j,m,n,ans,k,p,ll,rr,x,y,sum:longint;
6 procedure kp(ll,rr:longint);
7 var i,j,x,y:longint;
8 begin
9 i:=ll;j:=rr;
10 x:=l[(i+j)shr 1];
11 repeat
12 while l[i]<x do inc(i);
13 while l[j]>x do dec(j);
14 if i<=j then
15 begin
16 y:=l[i];
17 l[i]:=l[j];
18 l[j]:=y;
19 y:=s[i];
20 s[i]:=s[j];
21 s[j]:=y;
22 y:=t[i];
23 t[i]:=t[j];
24 t[j]:=y;
25 inc(i);
26 dec(j);
27 end;
28 until i>j;
29 if ll<j then kp(ll,j);
30 if i<rr then kp(i,rr);
31 end;
32 function find(x:longint):longint;
33 begin
34 if x<>f[x] then f[x]:=find(f[x]);
35 exit(f[x]);
36 end;
37 procedure dfs(i,j:longint);
38 var x,y:longint;
39 begin
40 if j=p then
41 begin
42 inc(g[rr]);
43 exit;
44 end;
45 if i=rr+1 then exit;
46 if rr-i+1+j<p then exit;
47 dfs(i+1,j);
48 x:=s[i];y:=t[i];
49 while x<>f[x] do x:=f[x];
50 while y<>f[y] do y:=f[y];
51 if x<>y then
52 begin
53 f[x]:=y;
54 dfs(i+1,j+1);
55 f[x]:=x;
56 end;
57 end;
58 begin
59 readln(n,m);
60 for i:=1 to m do readln(s[i],t[i],l[i]);
61 kp(1,m);
62 for i:=1 to n do f[i]:=i;
63 p:=0;sum:=0;
64 for i:=1 to m do
65 begin
66 x:=find(s[i]);y:=find(t[i]);
67 if x<>y then
68 begin
69 f[x]:=y;
70 inc(sum);
71 inc(p);
72 end;
73 if l[i]<>l[i+1] then
74 begin
75 ss[i]:=p;
76 p:=0;
77 end;
78 end;
79 if sum<n-1 then
80 begin
81 writeln(0);
82 halt;
83 end;
84 for i:=1 to n do f[i]:=i;
85 ll:=1;rr:=1;
86 for i:=1 to m do
87 begin
88 if l[i]=l[i+1] then
89 begin
90 inc(rr);
91 continue;
92 end;
93 p:=ss[rr];
94 dfs(ll,0);
95 for j:=ll to rr do
96 begin
97 x:=find(s[j]);y:=find(t[j]);
98 if x<>y then f[x]:=y;
99 end;
100 ll:=i+1;rr:=i+1;
101 end;
102 ans:=1;
103 for i:=1 to m do
104 if l[i]<>l[i+1] then ans:=ans*g[i] mod 31011;
105 writeln(ans);
106 end.