P4208 [JSOI2008]最小生成树计数

P4208 [JSOI2008]最小生成树计数


讲课的时候提到了这个妙极了的思想,于是就把这题做了。

有个结论:相同边权的边集不论怎么选,对最小生成树的贡献都相同(选的数量都会相同,而且连完之后图的连通性也会相同)

所以可以把不同边权的边分开来看,答案就是每种边权的方案之积。

于是就可以愉快地暴力了:

  1. 从小到大枚举边权;
  2. 拿出这个边权的所有边,并算出要连多少条边,以及连接这些边会连通哪些点;
  3. 暴力枚举选哪些边,加入方案里;
  4. 最后把现在通的集合缩起来。

复杂度上界是(O(m 2^{10} n)),然而远远达不到,因为(2^{10})最大也就是(C ^{5}_{10}),而且因为要缩点,n也不会一直是n。

emmm还有,注意判断无解情况

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cctype>
using namespace std;
typedef int mmp;
#define vd void
#define rg register
#define il inline
#define sta static
il int gi(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
namespace YYB{
    int n,m;
    struct edge{int u,v,w;}yyb[1001];
    int operator <(const edge&a,const edge&b){return a.w<b.w;}
    int fa[101];
    il int find(int*fa,int x){return fa[x]==x?x:fa[x]=find(fa,fa[x]);}
    il bool Union(int*fa,int x,int y){
        x=find(fa,x),y=find(fa,y);
        if(x>y)swap(x,y);
        if(x!=y){fa[y]=x;return 1;}
        return 0;
    }
    il vd solve(){
        n=gi(),m=gi();
        for(rg int i=1;i<=m;++i)yyb[i].u=gi(),yyb[i].v=gi(),yyb[i].w=gi();
        sort(yyb+1,yyb+m+1);
        for(rg int i=1;i<=n;++i)fa[i]=i;
        int ans=1;
        for(rg int i=1;i<=m;++i){
            int j=i;while(yyb[j+1].w==yyb[i].w)++j;
            for(rg int s=i;s<=j;++s)yyb[s].u=find(fa,yyb[s].u),yyb[s].v=find(fa,yyb[s].v);
            int fafa[101],tot=0;
            for(rg int s=1;s<=n;++s)fafa[s]=s;
            for(rg int s=i;s<=j;++s)if(Union(fafa,yyb[s].u,yyb[s].v))++tot;
            int res=0;
            for(rg int s=0;s<1<<(j-i+1);++s){
                int cnt=0;for(rg int p=s;p;p-=p&-p)++cnt;
                if(cnt!=tot)continue;
                for(rg int q=1;q<=n;++q)fafa[q]=q;
                int tot_=0;
                for(rg int q=i;q<=j;++q)
                    if(s&(1<<q-i))
                        if(Union(fafa,yyb[q].u,yyb[q].v))++tot_;
                if(tot_==tot)++res;
            }
            for(rg int s=i;s<=j;++s)Union(fa,yyb[s].u,yyb[s].v);
            ans=ans*res%31011;
            i=j;
        }
        for(rg int i=1;i<=n;++i)if(find(fa,i)!=1)ans=0;
        printf("%d
",ans);
    }
}
mmp main(){
    //freopen("bzoj_1016.in","r",stdin);
    //freopen("bzoj_1016.out","w",stdout);
    YYB::solve();
    return 0;
}
原文地址:https://www.cnblogs.com/xzz_233/p/8656490.html