题目大意

  求一个加权无向图的最小生成树的个数。1<=n<=100; 1<=m<=1000,具有相同权值的边不会超过10条。

题解

  命题1 由构成最小生成树的边的边权从小到大排序后得到的序列是唯一的。

  证明:首先,改变边权为同一个$w$的边的排列顺序进行Kruskal会得到所有的最小生成树。对于一个边权$w$,令顺序改变前树中边权等于$w$的边集为$A$,顺序改变后树中边权等于$w$的边集为$B$,所有最小生成树中边权小于$w$的边的集合为$C$。若$|B|<|A|$,这意味着存在至少一条边$e\in B$,存在一个边集$D\subseteq A, |D|>1$,使得$\forall e\in D$,$D+C+\{e\}$中存在环。环的传递性是指:若$E$中无环,$\{e_1\}+E$中有环,在环内的边集为$E_1$,$\{e_2\}+E$中也有环,在环内的边集为$E_2$,则$\{e_1\}+\{e_2\}+E-E_1\times E_2$中也存在环。因此,元素个数至少为2的边集中$D$中必然存在环,为不可能现象。同理,$|B|>|A|$时,将$A,B$交换,也成立因此原命题成立。

  命题2 对于一个不一定为连通图的图,定义它的连通性不变,当且仅当对于图中的每一个节点,与它连通的点的集合都不变。对于任意一个最小生成树,若将其中所有边权大于等于$w$的边都删除,则得到的子图的连通性对任意一个最小生成树来说都是相同的。

  证明:本命题用数学归纳法证明比较清晰。当$w=0$时,树中没有任何边,显然命题成立。当$w-1$成立时,如果命题不成立,即所有边权等于$w$的边集$A$的排列顺序不同时,图的连通性不同,那么由连通性的定义,存在一对点$u,v$,使得Kruskal枚举到边权$w-1$时它们不连通,且边权枚举到$w$时,排序方式1使得$u, v$连通,排序方式2使得$u, v$不连通,那么排序方式2的情况是肯定不存在的,因为排序方式2中也会枚举到排列方式1中使$u, v$连通的边的,这条边没有理由不加入图中。因此,原命题成立。

  上面的命题说明,不同的最小生成树的差别就在于边权相等的边集内选哪个了。题目说具有相同权值的边不会超过10条,所以我们先总体Kruskal看看对于每个边权都用到了多少条边,随后在一个个边权相等的边集中枚举组合即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std; const int MAX_NODE = 110, MAX_EDGE = 1010, P = 31011;
int EWCnt[MAX_EDGE], EWIntreeCnt[MAX_EDGE];
long long Ans; struct Node
{
Node *PrevFather, *Father;
}_nodes[MAX_NODE];
int TotNode; struct Edge
{
Node *From, *To;
int Weight; bool operator < (const Edge& a) const
{
return Weight < a.Weight;
}
}_edges[MAX_EDGE];
int TotEdge; struct Discretion
{
private:
int OrgData[MAX_EDGE], Rank[MAX_EDGE];
int N; int LowerBound(int k)
{
int l = 1, r = N;
while (l < r)
{
int mid = (l + r) / 2;
if (k <= OrgData[mid])
r = mid;
else
l = mid + 1;
}
return l;
} public:
void Push(int val)
{
OrgData[++N] = val;
} void Init()
{
sort(OrgData + 1, OrgData + N + 1);
OrgData[0] = -1;
int curRank = 0;
for (int i = 1; i <= N; i++)
Rank[i] = OrgData[i] == OrgData[i - 1] ? curRank : ++curRank;
} int GetRank(int val)
{
return Rank[LowerBound(val)];
}
}d; void Read()
{
scanf("%d%d", &TotNode, &TotEdge);
for (int i = 1; i <= TotEdge; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
_edges[i].From = _nodes + u;
_edges[i].To = _nodes + v;
_edges[i].Weight = w;
}
} void Discrete()
{
for (int i = 1; i <= TotEdge; i++)
d.Push(_edges[i].Weight);
d.Init();
for (int i = 1; i <= TotEdge; i++)
{
_edges[i].Weight = d.GetRank(_edges[i].Weight);
EWCnt[_edges[i].Weight]++;
}
} void InitGraph()
{
for (int i = 1; i <= TotNode; i++)
_nodes[i].PrevFather = _nodes[i].Father = _nodes + i;
} Node *FindRoot(Node *cur)
{
return cur->Father == cur ? cur : cur->Father = FindRoot(cur->Father);
} bool Join(Edge *e)
{
Node *root1 = FindRoot(e->From), *root2 = FindRoot(e->To);
if (root1 != root2)
{
root1->Father = root2;
return true;
}
else
return false;
} void Kruskal(int begin, int end, bool op)
{
for (int i = begin; i <= end; i++)
if (Join(_edges + i))
EWIntreeCnt[_edges[i].Weight] += op;
} void Fa_Prev_Cur()
{
for (int i = 1; i <= TotNode; i++)
_nodes[i].Father = _nodes[i].PrevFather;
} void Fa_Cur_Prev()
{
for (int i = 1; i <= TotNode; i++)
_nodes[i].PrevFather = _nodes[i].Father;
} int DoSth(vector<int>& chosen, int k)
{
Fa_Prev_Cur();
for (int i = 0; i < chosen.size(); i++)
if (!Join(_edges + k + chosen[i] - 1))
return 0;
return 1;
} int Combination(vector<int>& chosen, int n, int m, int begin, int k)
{
chosen.push_back(begin);
m--;
int ans = 0;
if (n - begin < m);
else if (m == 0)
ans = DoSth(chosen, k);
else
for (int i = begin + 1; i <= n; i++)
ans += Combination(chosen, n, m, i, k);
chosen.pop_back();
return ans;
} int Combination(int n, int m, int k)
{
int ans = 0;
static vector<int> chosen;
for (int i = 1; i <= n - m + 1; i++)
ans += Combination(chosen, n, m, i, k);
return ans;
} int GetAns()
{
int cur = 1;
Ans = 1;
while (cur <= TotEdge)
{
int curW = _edges[cur].Weight;
int cnt = Combination(EWCnt[curW], EWIntreeCnt[curW], cur);
Ans = Ans * (cnt + (cnt == 0)) % P;
Kruskal(cur, cur + EWCnt[curW] - 1, false);
Fa_Cur_Prev();
cur += EWCnt[curW];
}
return (int)Ans;
} bool NotConnect()
{
Node *root = FindRoot(_nodes + 1);
for (int i = 2; i <= TotNode; i++)
if (FindRoot(_nodes + i) != root)
return true;
return false;
} int main()
{
Read();
Discrete();
sort(_edges + 1, _edges + TotEdge + 1);
InitGraph();
Kruskal(1, TotEdge, true);
if (NotConnect())
{
printf("0\n");
return 0;
}
InitGraph();
printf("%d\n", GetAns());
return 0;
}

  

luogu4218 [JSOI2008] 最小生成树计数的更多相关文章

  1. bzoj1016 [JSOI2008]最小生成树计数

    1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3517  Solved: 1396[Submit][St ...

  2. BZOJ 1016: [JSOI2008]最小生成树计数( kruskal + dfs )

    不同最小生成树中权值相同的边数量是一定的, 而且他们对连通性的贡献是一样的.对权值相同的边放在一起(至多10), 暴搜他们有多少种方案, 然后乘法原理. ----------------------- ...

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

    1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 6200  Solved: 2518[Submit][St ...

  4. 【BZOJ 1016】 1016: [JSOI2008]最小生成树计数 (DFS|矩阵树定理)

    1016: [JSOI2008]最小生成树计数 Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树 ...

  5. 【bzoj1016】[JSOI2008]最小生成树计数

    1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 4863  Solved: 1973[Submit][St ...

  6. bzoj1016: [JSOI2008]最小生成树计数(kruskal+dfs)

    1016: [JSOI2008]最小生成树计数 题目:传送门 题解: 神题神题%%% 据说最小生成树有两个神奇的定理: 1.权值相等的边在不同方案数中边数相等  就是说如果一种方案中权值为1的边有n条 ...

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

    http://www.lydsy.com/JudgeOnline/problem.php?id=1016 (题目链接) 题意 求图的最小生成树计数. Solution %了下题解,发现要写矩阵树,15 ...

  8. [BZOJ]1016 JSOI2008 最小生成树计数

    最小生成树计数 题目描述 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同 ...

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

    最小生成树计数 Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小 ...

随机推荐

  1. 设计模式——“signleton”

    那天别人问了我一个问题,关于单例模式的,由于之前了解的都是蜻蜓点水,所以重新复习了一次重新总结. 单例模式的写法总的来说有5种:懒汉,恶汉,枚举,双重校验锁,静态内部类 懒汉 public class ...

  2. 【转载】HTTP 请求头与请求体

    原文地址: https://segmentfault.com/a/1190000006689767 HTTP Request HTTP 的请求报文分为三个部分 请求行.请求头和请求体,格式如图:一个典 ...

  3. JavaScript面试题链接汇总

    最新JavaScript笔试题(含答案) - 爱思资源网 前端工程师面试问题列表 - 爱思资源网 腾讯最新前端面试题记录分享 - 爱思资源网 优酷前端JS部分面试题 - 爱思资源网 百度校园招聘web ...

  4. IOS内购--后台PHP认证

    参考网址:https://blog.csdn.net/que_csdn/article/details/80861408 http://www.php.cn/php-weizijiaocheng-39 ...

  5. Unity如何播放带有alpha 通道的视频

    问题: 当使用Video Player播放带有alpha 通道的视频时带有黑色背景 解决方式: 使用文件格式为WEBM的视频,对视频文件进行的修改 在RawImage中,将New Render Tex ...

  6. 【python】详解numpy库与pandas库axis=0,axis= 1轴的用法

    对数据进行操作时,经常需要在横轴方向或者数轴方向对数据进行操作,这时需要设定参数axis的值: axis = 0 代表对横轴操作,也就是第0轴: axis = 1 代表对纵轴操作,也就是第1轴: nu ...

  7. [ZJOJ] 5794 2018.08.10【2018提高组】模拟A组&省选 旅行

    Description 悠悠岁月,不知不觉,距那传说中的pppfish晋级泡泡帝已是过 去数十年.数十年 中,这颗泡泡树上,也是再度变得精彩,各种泡泡 天才辈出,惊艳世人,然而,似乎 不论后人如何的出 ...

  8. uva 1583 Digit Generator(Uva-1583)

    题目不再写入了,vj:https://vjudge.net/problem/UVA-1583#author=0 主要讲的是找一个数的小于它的一个数,小于它的那个数每一位加起来再加上那个数就会等于原来的 ...

  9. 我理解的数据结构(一)—— 数组(Array)

    我理解的数据结构(一)-- 数组(Array) 首先,我是一个phper,但是毕竟php是一个脚本语言,如果使用脚本语言去理解数据结构具有一定的局限性.因为脚本语言是不需要编译的,如果你的语法写的不错 ...

  10. python爬虫19 | 遇到需要的登录的网站怎么办?用这3招轻松搞定!

    你好 由于你是游客 无法查看本文 请你登录再进 谢谢合作 当你在爬某些网站的时候 需要你登录才可以获取数据 咋整? 莫慌 小帅b把这几招传授给你 让你以后从容应对 那么 接下来就是 学习 python ...