• BZOJ 1016[JSOI2008]最小生成树计数


    问一个图最小生成树的个数,n<100,m<1000,规定相同权值的边不超过10条。

    每天午觉起来很长一段时间都仿佛活在梦中。上午看的下午来打,狂RE不止,发现一种边只有一条的情况没有r会GG。。

    //Twenty
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    typedef long long LL;
    const int maxn=100+5;
    const int maxm=1000+5;
    const int mod=31011;
    using namespace std;
    int ans=1,sum,n,m,fa[maxn],ecnt,tot;
    struct edge{
        int u,v,l,r,w,o;
        friend bool operator <(const edge &A,const edge &B) { 
            return A.w<B.w;
        }
    }e[maxm],ee[maxm];
    void init() {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++) 
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 
    }
    int find(int x) {return x==fa[x]?x:find(fa[x]);}
    void kruskal() {
        sort(e+1,e+m+1);
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m;i++) {
            int u=e[i].u,v=e[i].v;
            if(tot==n-1&&e[i].w!=ee[ecnt].w) break;
            u=find(u),v=find(v);
            if(!ecnt||e[i].w!=ee[ecnt].w) {
                    ee[++ecnt].l=i;
                    ee[ecnt].w=e[i].w;
            }
            ee[ecnt].r=i;
            if(u!=v) {
               fa[u]=v;
               tot++;
               ee[ecnt].o++;
            }
        }
    }
    void dfs(int id,int now,int k) {
        if(now==ee[id].r+1&&k==ee[id].o) sum++;
        if(now==ee[id].r+1) return ;
        int res=0;
        int u=find(e[now].u),v=find(e[now].v);
        if(u!=v) {
            fa[u]=v;
            dfs(id,now+1,k+1);
            fa[u]=u; fa[v]=v;
        }
        dfs(id,now+1,k);
    }
    void work() {
        kruskal();
        if(tot!=n-1) {printf("0"); return ;}
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=ecnt;i++) {
            sum=0;
            dfs(i,ee[i].l,0);
            ans=(ans*sum)%mod;
            for(int j=ee[i].l;j<=ee[i].r;j++) {
                int u=find(e[j].u),v=find(e[j].v);
                if(u!=v) fa[u]=v;
            }
        }
        printf("%d
    ",ans);
    }
    int main()
    {
        init();
        work();
        return 0;
    }
    View Code

    最小生成树的两个性质: 
    (1)不同的最小生成树中,每种权值的边出现的个数是确定的 
    (2)不同的生成树中,某一种权值的边连接完成后,形成的联通块状态是一样的 

    有了这两个性质,先跑一遍Kruskal求出最小生成树和每种权值的边分别出现的次数,因为相同权值的边很少,对于每种权值的边直接爆搜,乘法原理。

    若是没有这个次数限制,就要用矩阵树定理解决。

  • 相关阅读:
    【CH 5501】环路运输【DP】【单调队列】
    【CH 5501】环路运输【DP】【单调队列】
    【POJ 1456】Supermarket【并查集】
    【POJ 1456】Supermarket【并查集】
    【POJ 1456】Supermarket【并查集】
    【POJ 2411】Mondriaan's Dream【DP】
    数据结构实验之二叉树二:遍历二叉树
    数据结构实验之二叉树二:遍历二叉树
    36 静态数据成员与静态成员函数
    36 静态数据成员与静态成员函数
  • 原文地址:https://www.cnblogs.com/Achenchen/p/7581361.html
Copyright © 2020-2023  润新知