bzoj 1016: [JSOI2008]最小生成树计数
Description
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的
最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生
成树可能很多,所以你只需要输出方案数对31011的模就可以了。
solution
正解:kruskal+枚举
我们需要明白:对于所有的最小生成树满足:1.相同边权的边数相同。2.相同边权的边合并的点集也是一定的.
两个结论可以一起证明:边数显然不可能增加,不然在做第一次kruskal的时候肯定会被加入,如果边数还可以减少,那么有一些点肯定还没有被合并,那么合并可以使得边数+1.
所以我们枚举边权,因为相同边权的边不超过10,所以对每一种边权枚举,方案相乘即可
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=10005,mod=31011;
int fa[N],n,m,t=0;
struct node{
int x,y,z;
bool operator <(const node &pr)const{return z<pr.z;}
}e[N];
inline int find(int x){return fa[x]==x?x:find(fa[x]);}
int l[N],r[N],v[N];
bool priwork(){
int x,y,cnt=0;
for(int i=1;i<=m;i++){
if(e[i].z!=e[i-1].z)t++,l[t]=i;
r[t]=i;
x=e[i].x;y=e[i].y;
if(find(x)==find(y))continue;
fa[find(y)]=find(x);
cnt++;v[t]++;
}
if(cnt!=n-1)return false;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(find(i)!=find(j))return false;
return true;
}
int cnt=0;
inline void dfs(int i,int x,int sum){
if(x>r[i]){if(sum==v[i])cnt++;return ;}
dfs(i,x+1,sum);
int a=find(e[x].x),b=find(e[x].y);
if(a!=b){
fa[b]=a;
dfs(i,x+1,sum+1);
fa[b]=b;fa[a]=a;
}
}
void work()
{
cin>>n>>m;
for(int i=1;i<=m;i++)cin>>e[i].x>>e[i].y>>e[i].z;
for(int i=1;i<=n;i++)fa[i]=i;
sort(e+1,e+m+1);
if(!priwork()){puts("0");return ;}
int ans=1,x,y;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=t;i++){
cnt=0;
dfs(i,l[i],0);
ans*=cnt;if(ans>=mod)ans%=mod;
for(int j=l[i];j<=r[i];j++){
x=e[j].x;y=e[j].y;
if(find(x)==find(y))continue;
fa[find(y)]=find(x);
}
}
cout<<ans<<endl;
}
int main()
{
work();
return 0;
}