BZOJ 1016: [JSOI2008]最小生成树计数 矩阵树定理+kruskal

这道题很巧妙啊.   

有两个性质: 

1.一个图的最小生成树的每种边权数量是相等的.  

2.有 1 得,如果任意一个最小生成树中边权为 $v$ 的边都断掉,$(x,y)$ 连通性在任意 MST 中都相等.   

所以我们的做法就是先求出最小生成树,然后分别将每种边权 $v_{i}$ 从最小生成树中都断掉,得到若干联通块,将联通块看作点,再将所有边权为 $v_{i}$ 的重新连回去,跑一个矩阵树定理即可.   

code:

#include <cstdio>
#include <cstring> 
#include <map>  
#include <vector>   
#include <algorithm>  
#define N 103  
#define mod 31011
#define ll long long   
#define setIO(s) freopen(s".in","r",stdin)
using namespace std; 
struct UFS
{ 
    int p[N];   
    void clr() { for(int i=0;i<N;++i) p[i]=i; }   
    int find(int x) { return p[x]==x?x:p[x]=find(p[x]);  } 
    int merge(int x,int y) 
    {
        x=find(x),y=find(y); 
        if(x==y) return 0; 
        p[x]=y; 
        return 1;   
    }
}con;  
int n,m,cnt,col;        
int a[N][N],mark[N*N],val[N],id[N];        
struct Edge 
{ 
    int x,y,w; 
    Edge(int x=0,int y=0,int w=0):x(x),y(y),w(w){}   
    bool operator<(Edge b) const { return w<b.w; }                     
}tr[N*N];            
int gauss()
{
    int i,j,k,ans=1;   
    for(i=1;i<cnt;++i)
    {   
        for(j=i+1;j<cnt;++j)   
        {
            while(a[j][i])   
            {
                int t=a[i][i]/a[j][i];      
                for(k=i;k<cnt;++k)   a[i][k]=(ll)(a[i][k]%mod-(ll)t*a[j][k]%mod+mod)%mod;   
                swap(a[i],a[j]),ans*=-1;  
            }
        }
        if(!a[i][i]) return 0; 
    }
    if(ans<0) ans+=mod;  
    for(i=1;i<cnt;++i) ans=(ll)((ll)ans*a[i][i]%mod+mod)%mod;   
    return ans;      
}    
int kruskal() 
{  
    sort(tr+1,tr+1+m);    
    int i,j,siz=0;    
    con.clr();  
    for(i=1;i<=m;++i) 
    {
        if(con.merge(tr[i].x,tr[i].y)) 
        {
            ++siz;    
            mark[i]=1; 
            if(val[col]!=tr[i].w) val[++col]=tr[i].w;        
        }
    }                
    return siz==n-1;     
}
void link_edge(int v) 
{     
    cnt=0;  
    con.clr();      
    int i,j;  
    for(i=1;i<=m;++i) if(mark[i]&&tr[i].w!=v) con.merge(tr[i].x,tr[i].y);    
    for(i=1;i<=n;++i) if(con.find(i)==i) id[i]=++cnt; 
    for(i=1;i<=n;++i) id[i]=id[con.find(i)];           
    for(i=1;i<=m;++i) if(tr[i].w==v) 
    {
        int x=tr[i].x,y=tr[i].y;           
        if(id[x]==id[y]) continue;      
        x=id[x],y=id[y];    
        ++a[x][x],++a[y][y];   
        --a[x][y],--a[y][x];    
    }
}
int main() 
{
    // setIO("input");   
    int i,j;  
    scanf("%d%d",&n,&m);    
    for(i=1;i<=m;++i) scanf("%d%d%d",&tr[i].x,&tr[i].y,&tr[i].w);
    if(!kruskal())   { printf("0
"); return 0;  }          
    int ans=1;  
    for(i=1;i<=col;++i) 
    { 
        memset(a,0,sizeof(a));    
        link_edge(val[i]);      
        ans=(ll)ans*gauss()%mod;     
    }
    printf("%d
",ans);    
    return 0; 
}

  

原文地址:https://www.cnblogs.com/guangheli/p/12257939.html