码迷,mamicode.com
首页 > Web开发 > 详细

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

时间:2017-09-23 16:14:59      阅读:201      评论:0      收藏:0      [点我收藏+]

标签:splay   str   int   scanf   long   net   mat   mes   kruskal   

问一个图最小生成树的个数,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\n",ans);
}
int main()
{
    init();
    work();
    return 0;
}
View Code

 

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

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

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

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

标签:splay   str   int   scanf   long   net   mat   mes   kruskal   

原文地址:http://www.cnblogs.com/Achenchen/p/7581361.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!