zoukankan      html  css  js  c++  java
  • 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; 
    }
    

      

  • 相关阅读:
    [51nod1299]监狱逃离
    [51nod1206]Picture
    noi 2016 游记
    [Codeforces 696D] Legen...
    [bzoj2574] [Poi1999]Store-Keeper
    [bzoj1227] [SDOI2009]虔诚的墓主人
    [bzoj3979] [WF2012]infiltration
    Docker
    SpringBoot实现登录
    SpringBoot第一次案例
  • 原文地址:https://www.cnblogs.com/guangheli/p/12257939.html
Copyright © 2011-2022 走看看