BZOJ_1016_[JSOI2008]_最小生成树计数_(dfs+乘法原理)
描述
http://www.lydsy.com/JudgeOnline/problem.php?id=1016
给出一张图,其中具有相同权值的边的数目不超过10,求最小生成树的个数.
分析
生成树的计数有一个什么什么算法...
我真的企图研究了...但是智商捉急的我实在看不懂论文...
所以最后还是写了暴力...
当然暴力也要靠正确的姿势的.
首先来看一个结论:
同一张图的所有最小生成树中,边权值相同的边的数目是一定的.
也就是说,假如某一张图的某一棵最小生成树由边权值为1,1,2,2,2,3的边组成,那么这张图的所有其他最小生成树也都由六条权值分别为1,1,2,2,2,3的边组成.
至于证明,我们可以用数学归纳法.
1.以上结论对与n=2的情况显然成立.
2.假设对于n=k的情况以上结论成立即k个节点的最小生成树满足相同权值的边的数目一定.那么对于n=k+1的情况,需要加一条边将第k+1个点连起来,因为生成树是最小的,所以所加的边的权值是一定的,而之前的k-1条边(连接之前的k个点的边)各权值的边的数目也是一定的,所以最终各权值的边的数目是一定的.
有了这样的一个结论,又因为相同权值的边的数目不超过10,所以我们就可以利用暴力dfs+乘法原理,统计每一种权值的边的选择的方案数,然后相乘即可.
#include <bits/stdc++.h> using namespace std; const int maxn=100+5,maxm=1000+5,mod=31011; struct edge{ int u,v,w; edge(){} edge(int u,int v,int w):u(u),v(v),w(w){} bool operator < (const edge &a) const { return w<a.w; } }g[maxm]; int n,m,cnt,sum,ans=1; int a[maxm],b[maxm],ect[maxm],f[maxn]; inline int read(int &x){x=0;int k=1;char c;for(c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')k=-1;for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';return x*=k;} inline int find(int x){return x==f[x]?x:find(f[x]);} void dfs(int s,int t,int rem){ if(rem==0){ sum++; return; } for(int i=s;i-1+rem<=t;i++){ int fv=find(g[i].v),fu=find(g[i].u); if(fu!=fv){ int F=f[fu]; f[fu]=fv; dfs(i+1,t,rem-1); f[fu]=F; } } } inline void solve(){ int i,j; for(i=1,j=1;i<=m&&j<n;i++){ int fu=find(g[i].u),fv=find(g[i].v); if(fu!=fv){ f[fu]=fv; ect[b[i]]++; j++; } } if(j<n){ puts("0"); return; } for(int i=1;i<=n;i++) f[i]=i; for(i=1;i<=cnt;i++){ sum=0; dfs(a[i],a[i+1]-1,ect[i]); for(int j=a[i];j<a[i+1];j++){ int fu=find(g[j].u),fv=find(g[j].v); if(fu!=fv) f[fu]=fv; } ans=(ans*sum)%mod; } printf("%d\n",ans); } inline void init(){ read(n); read(m); for(int i=1;i<=n;i++) f[i]=i; for(int i=1,u,v,w;i<=m;i++){ read(u); read(v); read(w); g[i]=edge(u,v,w); } sort(g+1,g+1+m); for(int i=1;i<=m;i++){ if(g[i].w!=g[i-1].w) a[++cnt]=i; b[i]=cnt; } a[cnt+1]=m+1; } int main(){ init(); solve(); return 0; }View Code
1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 4666 Solved: 1890
[Submit][Status][Discuss]
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,0
00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。
Output
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
Sample Input
4 61 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8HINT
Source
- jsp: ServletContext
- javascript中的事件学习总结
- JavaScript事件
- 12-002-5 在javascript中实现DOM解析操作
- javascript闭包的理解
- JavaScript异常处理和事件处理
- js使用userAgent判断浏览器信息
- js使用userAgent判断浏览器信息
- 也许是最简洁好用的timeago.js库了
- JavaScript基础
- 动画 方块旋转
- iShare.js分享插件
- iShare.js分享插件
- 不会全排列算法(Javascript实现),我教你呀!
- css3+js+html实现模拟时钟
- Retrofit 2.0 超能实践(三),轻松实现文件/多图片上传/Json字符串
- JSON在线编辑器V2.0
- JavaScript总结
- 原生js的各种方法
- jsp和servlet