题目传送

  前置知识:对于同一个图的所有最小生成树,权值相等的边的数量相同。

  可以简单证明一下:

    我们可以从kruskal的过程考虑。这个算法把所有边按权值大小从小到大排序,然后按顺序看每条边,只要加上这条边后不会形成连通块,就加上。

    以上过程其实等价于先将所有权值等于第一条边的边都加进图中,然后一个个删边,使图中无环。设权值等于第一条边的边数为i,下次再将所有权值等于第i+1条边的边都加进图中。。。直至算过最后一条边,或图中刚好剩下了n-1条边(n为图的点的个数)。

    发现加完一批边后要删的边的个数等于形成的“最小环”的个数(这里最小环是指:对于一个最小环,不存在一组边使得通过这组边把环“从中间切开”后,被切开的环的两部分可与这组边形成两个新环(即不是依照国际标准的定义,而是为了方便在本文现定义的);同时最小环边数不一定小)。

    为什么呢?从一个最小环开始考虑:

      若不存在其他的某个最小环v与这个环u有公共边,那么只要任意删一条边就能减少一个最小环。

      若存在,这时删边就有两种情况:

        删公共边:首先u和v原来的最小环形态都会被破坏,最小环数目-2。然后,发现u和v剩下的部分又可以组成一个新的最小环,所以最小环的数目又+1。所以最小环数目-1;

        不删公共边:u的最小环形态被破坏,且不会生成新的最小环,所以最小环数目-1。

      综上,可知要删的边的数目==最小环的数目,且要删的边可是最小环上的任意边。

    由于加完一批边后,最小环的数目确定,所以删的边的数目也确定。故图生成的所有最小生成树边权相等的边数目也相等。所以我们可以先跑一次最小生成树,记录下每种边权在最小生成树中的出现次数

  同时我们还发现,当处理完一批权值等于x的边后,这个图的连通性(即都有哪些点连通)是唯一的。即使不用kruskal做最小生成树,设用了算法A做最小生成树,如果只保留权值等于x的边,那么保留的图的连通性与用kruskal做到处理完权值等于x的边时是一样的。否则,只可能连通的点数小于用kruskal做到时的连通的点数(因为kruskal全部地考虑过了权值等于x的边,其他算法不可能比全部还多吧)。但这是不成立的,因为若成立,就说明用kruskal做的最小生成树X中权值等于x的边比A算法得到的最小生成树Y中权值等于x的边的边数多一。由于kruskal是从小边开始贪心考虑所有边的,那么X的权值和一定小于Y,与Y是最小生成树矛盾;并且这也与上文的定理矛盾。

  设最小生成树边的权值从小到大分别为x1,x2,...,xk,那么构造一个最小生成树只需要分k步,每步都是在前面步骤都做了的基础上,选择一个对所有权值等于x的边的保留方案。由于每步造成的对连通性的影响都是一样的,即每步的结果都是一样的,所以可以用乘法原理,将每步的保留方案数乘起来再去模就是答案了。

  怎么求每步的保留方案数呢?由于题目限制权值相等的边不超过10条,所以用dfs枚举就是了,同时可用并查集验证可行性。注意:如果dfs要回溯到之前状态,那么并查集不能路径压缩,否则并查集的状态难以回溯到之前的状态。

代码:

 #include<iostream>
#include<cstdio>
#include<algorithm> using namespace std; const int N=,M=,mod=; struct Edge{
int from,to,len;
}e[M]; int n,m,f[N],l[M],r[M],cnt,tot,ecnt[M];
int x; char ch; inline int read()
{
x=;
ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<)+(x<<)+(ch^),ch=getchar();
return x;
} inline bool cmp(const Edge &a,const Edge &b)
{
return a.len<b.len;
} int yfa(int u)
{
if(u==f[u])
return u;
else
return f[u]=yfa(f[u]);
} int fa(int u)
{
if(u==f[u])
return u;
else
return fa(f[u]);
}
//能/不能路径压缩的并查集查找
void dfs(int wei,int kin,int had)//当前看的边数组的位置,当前看的边的种类,当前已经取的边的数目
{
int b1,b2;
b1=fa(e[wei].from);//dfs要回溯状态,所以dfs里并查集查找操作不能路径压缩
b2=fa(e[wei].to);
if(r[kin]-wei++had==ecnt[kin])//如果已经取的边的数目+还能取的数目=要取的边的数目,就只能取了
{
if(b1==b2) return;
if(had==ecnt[kin]-)
{
cnt++;
return;
}
f[b1]=b2;
dfs(wei+,kin,had+);
f[b1]=b1;
return;
}
if(b1!=b2)
{
if(had+==ecnt[kin])
cnt++;
else
{
f[b1]=b2;
dfs(wei+,kin,had+);
f[b1]=b1;
}
}
dfs(wei+,kin,had);
} int main()
{
n=read(),m=read();
for(int i=;i<=m;++i)
e[i].from=read(),e[i].to=read(),e[i].len=read();
sort(e+,e+m+,cmp);
for(int i=;i<=n;++i)
f[i]=i;
int u,v;
for(int i=;i<=m;++i)
{
if(e[i].len==e[i-].len)
{
r[tot]++;
}
else
{
++tot;
l[tot]=r[tot]=i;
}
if(cnt<n-)
{
u=yfa(e[i].from),v=yfa(e[i].to);
if(u!=v)
{
ecnt[tot]++;
f[u]=v;
cnt++;
}
}
}
if(cnt<n-)//注意无解时的判断
{
printf("");
return ;
}
long long ans=;
for(int i=;i<=n;++i) f[i]=i;
for(int i=;i<=tot;++i)
if(ecnt[i])
{
cnt=;
dfs(l[i],i,);
ans=(ans*cnt)%mod;
for(int j=l[i];j<=r[i];++j)//更新当前步骤做完时图的连通性
{
u=yfa(e[j].from);
v=yfa(e[j].to);
if(u!=v)
f[u]=v;
}
}
printf("%lld",ans);
return ;
}

洛谷P4208 [JSOI2008]最小生成树计数——题解的更多相关文章

  1. [洛谷P4208][JSOI2008]最小生成树计数

    题目大意:有$n$个点和$m$条边(最多有$10$条边边权相同),求最小生成树个数 题解:对于所有最小生成树,每种边权的边数是一样的.于是就可以求出每种边权在最小生成树中的个数,枚举这种边的边集,求出 ...

  2. Solution -「JSOI2008」「洛谷 P4208」最小生成树计数

    \(\mathcal{Description}\)   link.   给定带权简单无向图,求其最小生成树个数.   顶点数 \(n\le10^2\),边数 \(m\le10^3\),相同边权的边数不 ...

  3. 洛谷4208 JSOI2008最小生成树计数(矩阵树定理+高斯消元)

    qwq 这个题目真的是很好的一个题啊 qwq 其实一开始想这个题,肯定是无从下手. 首先,我们会发现,对于无向图的一个最小生成树来说,只有当存在一些边与内部的某些边权值相同的时候且能等效替代的时候,才 ...

  4. Luogu P4208 [JSOI2008]最小生成树计数

    题意 给定一个 \(n\) 个点 \(m\) 条边的图,求最小生成树的个数. \(\texttt{Data Range:}1\leq n\leq 100,1\leq m\leq 10^4\) 题解 一 ...

  5. 洛谷P1144最短路计数题解

    最短路计数 此题还是寻找从1到i点总共有几个最短路且每条边的边长为1,对于这种寻找最短路的个数,我们可以反向搜索,即先用\(SPFA\)预处理出所有点的最短路,然后我们反向记忆化搜索,可以用\(sum ...

  6. P4208 [JSOI2008]最小生成树计数

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

  7. BZOJ1016:[JSOI2008]最小生成树计数——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=1016 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不 ...

  8. 洛谷 P1144 最短路计数 题解

    P1144 最短路计数 题目描述 给出一个\(N\)个顶点\(M\)条边的无向无权图,顶点编号为\(1-N\).问从顶点\(1\)开始,到其他每个点的最短路有几条. 输入格式 第一行包含\(2\)个正 ...

  9. 洛谷 P4071 [SDOI2016]排列计数 题解

    P4071 [SDOI2016]排列计数 题目描述 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳 ...

随机推荐

  1. 第八周课程报告&&实验报告六

    Java实验报告 班级 计科一班 学号 20188390 姓名 宋志豪 实验四 类的继承 实验目的 理解异常的基本概念: 掌握异常处理方法及熟悉常见异常的捕获方法. 实验要求 练习捕获异常.声明异常. ...

  2. PostgreSQL创建只读账户

    目前PostgreSQL并不能像MySQL一样直接对某个数据库赋予只读权限,现实中有研发需要新建一个用户然后赋予对某个数据库只读权限. 举例说明如何创建 用edbstore用户连接edbstore数据 ...

  3. String,StringBuffer,StringBulider 三者的区别

    1.String 是字符串常量,StringBuffer 和StringBuilder 是字符串变量. 2.运行速度 StringBuilder > StringBuffer > Stri ...

  4. 【转】mysql用sql实现split函数

    关键词:mysql split mysql根据逗号将一行数据拆分成多行数据1.原始数据演示 2.处理结果演示 3.sql语句 SELECT a.id , a.NAME , substring_inde ...

  5. ORACLE 使用通配符进行字符串截取

    ORACLE 使用通配符进行字符串截取 select regexp_substr('aa--a(1-23),b---b(32---1)','[^(,)]+',1,1) as col1, regexp_ ...

  6. Doker GRPC "Connection reset by peer"

    https://success.docker.com/article/ipvs-connection-timeout-issue https://forums.docker.com/t/setting ...

  7. npm学习(七)之如何发布包、更新发布包、删除发布包

    前言 我们经常使用npm来下载别人的模块或者说包,那么我们如何将自己写的模块上传到npm呢? 了解npm政策 在开始之前,最好回顾一下npm的政策,以防您对站点礼仪.命名.许可或其他指导原则有疑问. ...

  8. TensorFlow基础与实战

    开源工具 TensorFlow:谷歌,C++.Python,Linux.Windows.Mac OS X.Andriod.iOS Caffe:加州大学,C++.Python.Matlab,Linux. ...

  9. roll out项目中原系统自定义程序不能编辑

    1.SE38编辑时报 程序源系统与现阶段系统不一致:Carry out repairs in non-original systems only if urgent 2.SE16N 查看表TADIR, ...

  10. 【抓包工具】使用Fiddler关于“由于目标计算机积极拒绝,无法连接。”的解决方案

    今天使用Fiddler的时候遇到下面这个问题:在地址栏想打开个一般处理程序,出现连接本机失败的提示,如下图: 而这在我没打开Fiddler的时候是显示正常的. 查看Fiddler,在嗅探 -> ...