BZOJ 1016 最小生成树计数

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

HINT

Source

这题要猜一个结论——长为i的边个数是一定的以及前i小的边他们构成的并查集是一定的,这样就可以 2^n dfs了(相同长度的边<=10)。

 1 #include<algorithm>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 using namespace std;
 6
 7 #define maxn (110)
 8 #define maxm (1010)
 9 #define rhl (31011)
10
11 int father[maxn],save[maxn],bac[maxm],road[maxm];
12 int n,m,tot,ans,sum;
13 struct E{ int u,v,w; }edge[maxm];
14
15 inline void init() {for (int i = 1;i <= n;++i) father[i] = i;}
16
17 inline int find(int a) {if (father[a] != a) father[a] = find(father[a]); return father[a];}
18
19 inline bool cmp(E a,E b){ return a.w < b.w; }
20
21 inline void mst()
22 {
23     sort(edge+1,edge+m+1,cmp); init();
24     int have = 0,r1,r2,pos;
25     for (int i = 1;i <= m;++i)
26     {
27         r1 = find(edge[i].u),r2 = find(edge[i].v);
28         if (r1 != r2)
29         {
30             father[r1] = r2; ++have;
31             pos = lower_bound(bac+1,bac+tot+1,edge[i].w)-bac;
32             ++road[pos];
33         }
34         if (have == n - 1) break;
35     }
36     if (have < n - 1) printf("0"),exit(0);
37 }
38
39 inline void dfs(int a,int r,int pos,int cho)
40 {
41     if (road[pos] == cho)
42     {
43         ++sum;
44         if (sum == 1) memcpy(save,father,sizeof(save));
45         return;
46     }
47     if (a > r) return;
48     if (cho+r-a+1<road[pos]) return;
49     int temp[maxn];
50     dfs(a+1,r,pos,cho);
51     memcpy(temp,father,sizeof(temp));
52     int r1 = find(edge[a].u),r2 = find(edge[a].v);
53     if (r1 != r2) father[r1] = r2,dfs(a+1,r,pos,cho+1);
54     memcpy(father,temp,sizeof(temp));
55 }
56
57 int main()
58 {
59     freopen("1016.in","r",stdin);
60     freopen("1016.out","w",stdout);
61     scanf("%d %d",&n,&m);
62     for (int i = 1;i <= m;++i)
63     {
64         int a,b,c;
65         scanf("%d %d %d",&a,&b,&c);
66         edge[i] = (E) {a,b,c};
67         bac[i] = c;
68     }
69     sort(bac+1,bac+m+1);
70     tot = unique(bac+1,bac+m+1)-bac-1;
71     mst();
72     init(); ans = 1;
73     for (int i = 1;i <= m;)
74     {
75         int j = i;
76         while (j < m && edge[j+1].w == edge[i].w) ++j;
77         sum = 0;
78         dfs(i,j,lower_bound(bac+1,bac+tot,edge[i].w)-bac,0);
79         (ans *= sum)%=rhl;
80         memcpy(father,save,sizeof(save));
81         i = j+1;
82     }
83     printf("%d",ans);
84     fclose(stdin); fclose(stdout);
85     return 0;
86 }

时间: 2024-08-03 03:36:55

BZOJ 1016 最小生成树计数的相关文章

【BZOJ】【1016】【JSOI2008】最小生成树计数

Kruskal/并查集+枚举 唉我还是too naive,orz Hzwer 一开始我是想:最小生成树删掉一条边,再加上一条边仍是最小生成树,那么这两条边权值必须相等,但我也可以去掉两条权值为1和3的,再加上权值为2和2的,不也满足题意吗?事实上,如果这样的话……最小生成树应该是1和2,而不是1和3或2和2!!! 所以呢?所以对于一个图来说,最小生成树有几条边权为多少的边,都是固定的!所以我们可以做一遍Kruskal找出这些边权,以及每种边权出现的次数.然后,对于每种边权,比方说出现了$v_i$

[BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

题目链接:BZOJ - 1016 题目分析 最小生成树的两个性质: 同一个图的最小生成树,满足: 1)同一种权值的边的个数相等 2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等 这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法. 最后根据乘法原理将各种边的选法数乘起来就可以了. 特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路

BZOJ 题目1016: [JSOI2008]最小生成树计数(Kruskal+Matrix_Tree)

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 3569  Solved: 1425 [Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对3101

【BZOJ】1016: [JSOI2008]最小生成树计数 深搜+并查集

最小生成树计数 Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树 可能很多,所以你只需要输出方案数对31011的模就可以了. Input 第 一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数.每个节点用1~n的整数编号.接下来的m行,每行包含两个整数:a,

1016: [JSOI2008]最小生成树计数

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 6200  Solved: 2518[Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的 最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生 成树可能很多,所以你只需要输出方案数对3101

BZOJ_1016_[JSOI2008]_最小生成树计数_(dfs+乘法原理)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1016 给出一张图,其中具有相同权值的边的数目不超过10,求最小生成树的个数. 分析 生成树的计数有一个什么什么算法... 我真的企图研究了...但是智商捉急的我实在看不懂论文... 所以最后还是写了暴力... 当然暴力也要靠正确的姿势的. 首先来看一个结论: 同一张图的所有最小生成树中,边权值相同的边的数目是一定的. 也就是说,假如某一张图的某一棵最小生成树由边权值为1,1,2,2,2,3的

【bzoj1016】 JSOI2008—最小生成树计数

http://www.lydsy.com/JudgeOnline/problem.php?id=1016 (题目链接) 题意:求图的最小生成树计数. Solution  %了下题解,发现要写矩阵树,150++的程序什么鬼.于是就蒯了hzwer的简便方法.  将边按照权值大小排序,将权值相同的边分到一组,统计下每组分别用了多少条边.然后对于每一组进行dfs,判断是否能够用这一组中的其他边达到相同的效果.最后把每一组的方案数相乘就是答案.  注意并查集不要压缩路径,不然的话不好回溯. 代码: //

[JSOI2008][BZOJ1016] 最小生成树计数

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3379  Solved: 1336[Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的

最小生成树计数

Minimum Spanning Tree http://acm.hdu.edu.cn/showproblem.php?pid=4408 模板题 1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<algorithm> 5 #define mt(a,b) memset(a,b,sizeof(a)) 6 using namespace std; 7 typedef __int