zoukankan      html  css  js  c++  java
  • P4208 [JSOI2008]最小生成树计数

    先写了个垃圾版本。。。加强版先咕着


    思路:最小生成树的性质?+无脑搜索or矩阵树定理

    提交:4次(反思反思)

    题解:

    简单版:

    首先显然最小生成树相同权值的边的数量是不变的(否则就不是最小生成树);

    然后就是相同权值的边组成的连通块的状态是不变的(就是不管你从权值为$w$的边中选出哪几条,只要合法,连通块的状态都是一样的),即不同权值的边是互不干扰的。

    简单证明:(from 邓大佬)

    $tql$

    然后我们就可以先造出一颗最小生成树,记录每种权值出现的次数,然后对于相同权值的边直接搜索用哪几条,用非路径压缩的并查集维护连通性(便于恢复连通块状态),把答案乘起来取模,ok。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define R register int
    using namespace std;
    #define ull unsigned long long
    #define ll long long
    #define pause (for(R i=1;i<=10000000000;++i))
    #define In freopen("NOIPAK++.in","r",stdin)
    #define Out freopen("out.out","w",stdout)
    namespace Fread {
    static char B[1<<15],*S=B,*D=B;
    #ifndef JACK
    #define getchar() (S==D&&(D=(S=B)+fread(B,1,1<<15,stdin),S==D)?EOF:*S++)
    #endif
    inline int g() {
        R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
        if(ch==EOF) return EOF; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
    } inline bool isempty(const char& ch) {return (ch<=36||ch>=127);}
    inline void gs(char* s) {
        register char ch; while(isempty(ch=getchar()));
        do *s++=ch; while(!isempty(ch=getchar()));
    }
    } using Fread::g; using Fread::gs;
    namespace Luitaryi {
    const int N=110,M=1010,mod=31011;
    int n,m,i,cnt,tot;
    ll ans=1;
    int fa[N],l[M],r[M],c[M];
    struct edge { int u,v,w; edge() {}
        edge(int uu,int vv,int ww) {u=uu,v=vv,w=ww;}
        inline bool operator < (const edge& that) const {return w<that.w;}
    }e[M];
    inline int getf(int x) {return x==fa[x]?x:getf(fa[x]);}
    inline void dfs(int lst,int s) {
        if(lst==r[i]+1) {tot+=s==c[i]; return;}
        R u=e[lst].u,v=e[lst].v;
        R uf=getf(u),vf=getf(v);
        if(uf!=vf)
            fa[uf]=vf,dfs(lst+1,s+1),
            fa[uf]=uf,fa[vf]=vf;
        dfs(lst+1,s);
    }
    inline void main() {
        n=g(),m=g(); for(R i=1,u,v,w;i<=m;++i) u=g(),v=g(),w=g(),e[i]=edge(u,v,w);
        sort(e+1,e+m+1); for(R i=1;i<=n;++i) fa[i]=i;
        for(i=1;i<=m&&tot<n-1;++i) {
            if(e[i].w!=e[i-1].w) ++cnt,r[cnt-1]=i-1,l[cnt]=i;
            R u=e[i].u,v=e[i].v;
            R uf=getf(u),vf=getf(v);
            if(uf!=vf) ++tot,++c[cnt],fa[uf]=vf;
        } --i; while(i<m&&e[i+1].w==e[i].w) ++i; r[cnt]=i;
        if(tot<n-1) return (void)printf("0
    ");
        for(R i=1;i<=n;++i) fa[i]=i;
        for(i=1;i<=cnt;++i) {
            tot=0; dfs(l[i],0); ans=ans*tot%mod;
            for(R j=l[i];j<=r[i];++j) fa[getf(e[j].u)]=getf(e[j].v);
        } printf("%lld
    ",ans);
    }
    }
    signed main() {
        Luitaryi::main();
    }

    复杂版:

    发现:

    1.每种权值的边的数量是固定的。

    2.不同的生成树中,某一种权值的边任意加入需要的数量后,形成的联通块状态是一样的。

    这样一来,我们可以枚举树边的权值$i$,把权值不是$i$的树边都加入图中后进行缩点;对于权值为$i$ 的原图中的边,在缩点后的图中构造基尔霍夫矩阵,用矩阵树定理求出方案数。

    代码先咕着$qwq$


    2019.07.17

  • 相关阅读:
    浅谈图标布局
    和浏览器异步请求取消相关的那些事
    chrome浏览器的跨域设置——包括版本49前后两种设置
    cordova加载层、进度条、文件选择插件
    js构建ui的统一异常处理方案(四)
    js构建ui的统一异常处理方案(三)
    通过 IntelliJ IDEA 来 Debug Jar包
    JPA使用Specification like查询时特殊字符%和_处理问题 Escape示例
    java8新特性:利用Lambda处理List集合
    让开发部署提速的 IDEA 插件神器攻略(转)
  • 原文地址:https://www.cnblogs.com/Jackpei/p/11204385.html
Copyright © 2011-2022 走看看