您的位置:首页 > Web前端 > JavaScript

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

2016-06-24 23:05 453 查看

描述

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 MB
Submit: 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 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1

Sample Output

8

HINT

Source

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: