bzoj1016:[JSOI2008]最小生成树计数
思路:模拟kruskal的过程,可以发现对于所有权值相同的边,有很多种选择的方案,而且权值不同的边并不会相互影响,因为先考虑权值较小的边,权值比当前权值大的边显然不在考虑范围之内,而权值比当前权值小的边所组成的连通块已经经过缩点变成一个点了,因此处理权值相同的所有边可以看成是一个阶段,最后的答案也就是所有阶段的答案的乘积(乘法原理)。
那么如何来处理权值相同的方案数呢,同样还是考虑kruskal的过程,因为权值相同的边可能会组成很多个连通块,且连通块之间互不影响,因此只考虑单个连通块即可(还是乘法原理),如果一条边所连接的两个点不在一个连通块内,那么就把这条边算进答案,那么对于一个连通块kruskal的过程显然要让所有点连通,且所选的边恰好构成了一棵树,那么这样问题就转化成了如何求生成树的数量,利用matrix-tree定理,用高斯消元求解kirchhoff矩阵即可。
还有最后不要忘了判图中没有最小生成树的情况(图不连通)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxm 1005
#define maxn 105
#define mod 31011 int n,m,cnt,ans=1;
int fa[maxn],pos[maxn];
int K[maxn][maxn];
bool vis[maxn]; vector<int> v[maxn]; struct edge{
int from,to,val;
bool operator <(const edge &a)const{return val<a.val;}
}e[maxm]; inline int read(){
int x=0;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar());
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
} int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} int gauss(){
int t,n=cnt-1,ans=1,f=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
K[i][j]%=mod;
for (int i=1;i<n;i++){
for (t=i;t<=n;t++) if (K[t][i]) break;if (t>n) return 0;
if (t!=i){for (int j=1;j<=n;j++) swap(K[i][j],K[t][j]);f=-f;}
for (int j=i+1;j<=n;j++)
for (;K[j][i];){
int t=K[i][i]/K[j][i];
for (int k=i;k<=n;k++) K[i][k]=(K[i][k]-t*K[j][k])%mod;
for (int k=i;k<=n;k++) swap(K[i][k],K[j][k]);
f=-f;
}
}
for (int i=1;i<=n;i++) ans=1ll*ans*K[i][i]%mod;
return ans*f;
} void dfs(int x,int num){
K[num][num]=v[x].size(),pos[x]=num;
for (unsigned int i=0;i<v[x].size();i++){
vis[v[x][i]]=0;
if (!pos[v[x][i]]) pos[v[x][i]]=++cnt,K[num][pos[v[x][i]]]--,dfs(v[x][i],cnt);
else K[num][pos[v[x][i]]]--;
}
} int main(){
n=read(),m=read();
for (int i=1;i<=m;i++) e[i].from=read(),e[i].to=read(),e[i].val=read();
for (int i=1;i<=n;i++) fa[i]=i;
sort(e+1,e+m+1);
for (int i=1;i<=m+1;i++){
int ck=find(1);
for (int j=2;j<=n;j++) if (find(j)!=ck){ck=0;break;}
if (ck) break;
int x=find(e[i].from),y=find(e[i].to);
if (x!=y) vis[x]=1,vis[y]=1,v[x].push_back(y),v[y].push_back(x);
if (e[i].val!=e[i+1].val){
for (int j=1;j<=n;j++)
if (vis[j]){
for (int a=1;a<=cnt;a++)
for (int b=1;b<=cnt;b++)
K[a][b]=0;
memset(pos,0,sizeof(pos));
vis[j]=0,cnt=1,dfs(j,cnt);
ans=1ll*ans*gauss()%mod;
}
for (int j=1;j<=n;j++){
for (unsigned int k=0;k<v[j].size();k++){
int x=find(j),y=find(v[j][k]);
if (x!=y) fa[x]=y;
}
v[j].clear();
}
}
}
int check=find(1);
for (int i=2;i<=n;i++) if (find(i)!=check){check=0;break;}
printf("%d\n",check?ans:0);
return 0;
}
bzoj1016:[JSOI2008]最小生成树计数的更多相关文章
- bzoj1016 [JSOI2008]最小生成树计数
1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 3517 Solved: 1396[Submit][St ...
- bzoj1016: [JSOI2008]最小生成树计数(kruskal+dfs)
1016: [JSOI2008]最小生成树计数 题目:传送门 题解: 神题神题%%% 据说最小生成树有两个神奇的定理: 1.权值相等的边在不同方案数中边数相等 就是说如果一种方案中权值为1的边有n条 ...
- BZOJ1016:[JSOI2008]最小生成树计数(最小生成树,DFS)
Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...
- [bzoj1016][JSOI2008]最小生成树计数 (Kruskal + Matrix Tree 定理)
Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...
- 【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数
题意:求一个图的最小生成树个数. 矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值. 度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是 ...
- [BZOJ1016][JSOI2008]最小生成树计数(结论题)
题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1016 分析: 首先有个性质:如果边集E.E'都可以表示一个图G的最小生成树(当然E和E ...
- [BZOJ1016] [JSOI2008] 最小生成树计数 (Kruskal)
Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...
- 【最小生成树】BZOJ1016: [JSOI2008]最小生成树计数
Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...
- 2018.09.24 bzoj1016: [JSOI2008]最小生成树计数(并查集+搜索)
传送门 正解是并查集+矩阵树定理. 但由于数据范围小搜索也可以过. 我们需要知道最小生成树的两个性质: 不同的最小生成树中,每种权值的边出现的个数是确定的 不同的生成树中,某一种权值的边连接完成后,形 ...
- [BZOJ1016][JSOI2008]最小生成树计数 最小生成树 搜索
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1016 做这道题之前需要知道一些结论,同一个图的最小生成树中相同权值的边的个数是不会变的,如 ...
随机推荐
- matlab s变换
A4=readdata('E:\mydata.TXT');[st,t,f] = st(A4(1:1000,2)); surf(t,f,10*log10(abs(st)),'EdgeColor','no ...
- JavaScript(十一) HTML DOM - 改变CSS
HTML DOM 允许 JavaScript 改变 HTML 元素的样式. A.改变 HTML 样式 如需改变 HTML 元素的样式,请使用这个语法: document.getElementById( ...
- mac svn的替代品CornerStone
推荐mac CornerStone客户端图形软件,类似windows下svn tortoise. 一.下载地址 : http://bbs.feng.com/read-htm-tid-7936664.h ...
- ios 说一说UINavigationController 的堆栈
iOS的界面堆栈管理比android的要好用很多. 这里写两点:一点是 如何重回前面的vc,而不是push一个alloc的新界面.第二点就是判断当前堆栈显示的vc是何vc. vc堆栈: vc1-> ...
- 【转】linux中的cut/tr/join/split/xargs命令
1. cut命令 cut命令用于从文件或者标准输入中读取内容并截取每一行的特定部分并送到标准输出. 截取的方式有三种:一是按照字符位置,二是按照字节位置,三是使用一个分隔符将一行分割成多个field, ...
- C#-设置窗体在显示器居中显示
在窗体的属性中查看:StartPosition属性,该属性的设置中有一个"CenterScreen"的选择项,该项就是设置窗体局中显示的.
- Android ListView快速定位(四)
方法四: 添加一个EditText,作为搜索框 + Filter 其实这个不算第四个方法,因为与第二个一样,主要是实现Filter. 但是对于EditText的监听,我以前也没有写过,所以也记录一下. ...
- iOS开发——OC篇&协议篇/NSCoder/NSCoding/NSCoping
协议篇/NSCoder/NSCoding/NSCoping 协议声明类需要实现的的方法,为不同的类提供公用方法,一个类可以有多个协议,但只能有一个父类,即单继承.它类似java中的接口. 正式协议(f ...
- [000]socket通信--server和client实现的简单例子
在C语言中就学过scanf和printf,其实理解socket就跟这两个输入输出差不多,只不过是信息的传输而已. 1.TCP服务器端(server)的默认函数调用顺序: 按照上述的调用顺序,我们可以来 ...
- java_Cookies_1_商品浏览历史记录servlet2
public class CookiesServlet2 extends HttpServlet { // 显示商品详细信息 public void doGet(HttpServletRequest ...