HDU 4408 最小生成树的个数

题意很明确,就是求最小生成树的个数……

专攻图论也有段时间了,求个最小生成树和判唯一,以及其他的一些应用都还算比较熟练了,但今天这道题,,,真给跪了,,,觉得自己需要补充大量的图论的定理啊什么的。

废话少说,,做出来这题参考了这篇题解【矩阵树定理 + kruskal】http://blog.youkuaiyun.com/jarily/article/details/8902402

当时做的时候都打算用费用流了,然而构图半天并没有做出来。。。


#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>

using namespace std;
typedef long long ll;
const int maxn = 505;
const int maxm = 5005;

struct Edge{
    int u,v,cost;
    bool operator < (const Edge &z) const {
        return cost < z.cost;
    }
}edge[maxm];

int n,m,mod;
int par[maxn],fa[maxn];
vector<int> V[maxn];
ll C[maxn][maxn],D[maxn][maxn];
bool vis[maxn];

void init_union(){
    memset(D,0,sizeof(D));
    memset(vis,0,sizeof(vis));
    memset(par,-1,sizeof(par));
    memset(fa,-1,sizeof(fa));
}

int find(int x,int f[]){
    while(f[x] >= 0) x = f[x]; return x;
}

ll det(int len){
    ll ans=1;
    for(int i=0; i<len; i++)
    {
        for(int j=i+1; j<len; j++)
            while(C[i][j]!=0)
            {
                ll t=C[i][i]/C[i][j];
                for(int k=0; k<len; k++)
                    C[k][i]=(C[k][i]-(t*C[k][j])%mod)%mod;
                ll temp;
                for(int k=0; k<len; k++)
                {
                    temp=C[k][i];
                    C[k][i]=C[k][j];
                    C[k][j]=temp;
                }
                ans=-ans;
            }
        if(C[i][i]==0)
            return 0;
    }
    for(int i=0; i<len; i++)
        ans=(ans*C[i][i]) % mod;
    //printf("!!!!!!!\n");
    //cout<<ans<<endl;
    return (ans + mod) % mod;
}


void solve(){
    sort(edge,edge+m);
    init_union();

    int cur = -1;
    ll res = 1;
    int u,v;
    for(int k=0;k<=m;k++){
        if(k == m || cur != edge[k].cost){
            for(int i=1;i<=n;i++){
                if(vis[i]){
                    vis[i] = false;
                    u = find(i,fa);
                    V[u].push_back(i);
                }
            }
            for(int i=1;i<=n;i++){
                int len = V[i].size();
                if(len > 1){
                    for(int p=0;p<len;p++){
                        for(int q=0;q<len;q++)  C[p][q] = 0;
                    }
                    for(int p=0;p<len;p++){
                        for(int q=p+1;q<len;q++){
                            u = V[i][p];
                            v = V[i][q];
                            C[p][q] -= D[u][v];
                            C[q][p] = C[p][q];
                            C[p][p] += D[u][v];
                            C[q][q] += D[u][v];
                        }
                    }
                    res = (res*det(len-1)) % mod;
                    for(int p=0;p<len;p++) if(V[i][p] != i) par[V[i][p]] = i;
                }
                V[i].clear();
            }
            //for(int i=1;i<=n;i++){
                //if(i != find(i,par)) fa[i] = par[i] = find(i,par);

            //}
            if(k == m) break;
            cur = edge[k].cost;
        }

        u = find(edge[k].u,par);
        v = find(edge[k].v,par);
        if(u == v) continue;
        D[u][v]++;D[v][u]++;
        vis[u] = vis[v] = true;
        u = find(u,fa);v = find(v,fa);
        if(u != v)  fa[u] = v;
    }
    int cnt = 0;
    for(int i=1;i<=n;i++) if(par[i] == -1) cnt++;
    printf("%lld\n",(cnt != 1 || m == 0) ? 0 : res);
}

int main(){
    while(~scanf("%d%d%d",&n,&m,&mod)){
        if(n + m + mod == 0) break;
        for(int i=0;i<m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].cost);
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值