BZOJ 1016 [JSOI2008]最小生成树计数
1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 3529 Solved: 1404
[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,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过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
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8
HINT
Source
题解:同一个图的最小生成树,满足:
1)同一种权值的边的个数相等
2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等
这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法。
最后根据乘法原理将各种边的选法数乘起来就可以了。
特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路径压缩。
代码们:
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<queue> 6 #include<cstring> 7 #define PAU putchar(' ') 8 #define ENT putchar('\n') 9 using namespace std; 10 const int maxn=100+10,maxm=1000+10,inf=-1u>>1,mod=31011; 11 struct edge{int x,y,w;}e[maxm];struct data{int L,R,num;}a[maxm]; 12 int n,m,cnt,tot,ans=1,sum,fa[maxn]; 13 bool cmp(const edge&a,const edge&b){return a.w<b.w;} 14 int find(int x){return x==fa[x]?x:find(fa[x]);} 15 int findset(int x){return x==fa[x]?x:fa[x]=findset(fa[x]);} 16 void dfs(int x,int pos,int k){ 17 if(pos>a[x].R){if(k==a[x].num)sum++;return;} 18 int f1=find(e[pos].x),f2=find(e[pos].y); 19 if(f1!=f2)fa[f1]=f2,dfs(x,-~pos,-~k),fa[f1]=f1; 20 dfs(x,-~pos,k);return; 21 } 22 inline int read(){ 23 int x=0,sig=1;char ch=getchar(); 24 while(!isdigit(ch)){if(ch=='-')sig=-1;ch=getchar();} 25 while(isdigit(ch))x=10*x+ch-'0',ch=getchar(); 26 return x*=sig; 27 } 28 inline void write(int x){ 29 if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x; 30 int len=0,buf[15];while(x)buf[len++]=x%10,x/=10; 31 for(int i=len-1;i>=0;i--)putchar(buf[i]+'0');return; 32 } 33 void init(){ 34 n=read();m=read(); 35 for(int i=1;i<=n;i++) fa[i]=i; 36 for(int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].w=read(); 37 sort(e+1,e+m+1,cmp); 38 for(int i=1;i<=m;i++){ 39 if(e[i].w!=e[i-1].w){a[++cnt].L=i;a[cnt-1].R=i-1;} 40 int f1=findset(e[i].x),f2=findset(e[i].y); 41 if(f1!=f2){fa[f1]=f2;a[cnt].num++;tot++;} 42 } 43 return; 44 } 45 void work(){ 46 a[cnt].R=m; 47 if(tot<n-1){write(0);return;} 48 for(int i=1;i<=n;i++) fa[i]=i; 49 for(int i=1;i<=cnt;i++){ 50 sum=0;dfs(i,a[i].L,0); 51 ans=(ans*sum)%mod; 52 for(int j=a[i].L;j<=a[i].R;j++){ 53 int f1=find(e[j].x),f2=find(e[j].y); 54 if(f1!=f2)fa[f1]=f2; 55 } 56 }write(ans); 57 return; 58 } 59 void print(){ 60 return; 61 } 62 int main(){init();work();print();return 0;}