题目链接

题目大意:给定一张\(n\)个点\(m\)条边无向图,保证具有相同边权的边不超过\(10\)条,\(n \leq100,m\leq1000\),求不同最小生成树个数

最小生成树、计数


分析:

做这题需要用到一个结论:

所有最小生成树,把边权按照升序排列之后,得到的序列是一样的。也就是说对于最小生成树而言,每种边权的边选了多少条是确定的

此外,按照边权升序的顺序加边,把一种边权的边全部加完之后,所有生成树的连通性是相同的。

由于具有相同边权的边的数量较少我们可以暴力

先跑一遍最小生成树求出每种边权选择的数量,然后对每种边权暴力统计合法方案数,最后用乘法原理计算答案

判断两个图连通性是否相同可以使用并查集,如果\(A\)图每个点所在的联通块编号序列可以通过某种置换得到\(B\)图对应序列,那么\(A,B\)两个图的连通性就是相同的

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 128,maxm = 1024,mod = 31011;
inline int read(){
    int x = 0;char c = getchar();
    while(!isdigit(c))c = getchar();
    while(isdigit(c))x = x * 10 + c - '0',c = getchar();
    return x;
}
struct edge{
    int u,v,d;
    bool operator < (const edge &rhs)const{
        return d < rhs.d;
    }
}edges[maxm];
int n,m,tot,last,num,mem[16],trans[maxn],ans = 1,cnt;
struct mset{
    int f[maxn],sz;
    inline void init(){
        for(int i = 1;i <= n;i++)f[i] = i;
        sz = n;
    }
    inline int find(int x){return x == f[x] ? x : f[x] = find(f[x]);}
    inline void merge(int a,int b){
        int x = find(a),y = find(b);
        f[x] = y;sz--;
    }
}s,tmp,lass;
inline void out(){
    tmp = lass;
    for(int i = 1;i <= num;i++){
        int x = tmp.find(edges[mem[i]].u),y = tmp.find(edges[mem[i]].v);
        if(x == y)return;
        tmp.merge(x,y);
    }
    for(int i = 1;i <= n;i++)tmp.f[i] = tmp.find(tmp.f[i]);
    memset(trans,0,sizeof(trans));
    for(int i = 1;i <= n;i++){
        if(!trans[lass.f[i]])trans[lass.f[i]] = tmp.f[i];
        if(trans[lass.f[i]] != tmp.f[i])return;
    }
    cnt++;
}
inline void dfs(int now,int pos,int mx){
    if(pos == num + 1){
        out();
        return;   
    }
    if((mx - now + 1) < (num - pos + 1))return;
    mem[pos] = now;
    dfs(now + 1,pos + 1,mx);
    dfs(now + 1,pos,mx);
}
int main(){
    n = read(),m = read();
    for(int i = 1;i <= m;i++)edges[i].u = read(),edges[i].v = read(),edges[i].d = read();
    sort(edges + 1,edges + 1 + m);
    s.init();
    lass = s;
    for(int i = 1;i <= m + 1;i++){
        if(edges[i].d != edges[i - 1].d){
            for(int i = 1;i <= n;i++)s.f[i] = s.find(s.f[i]);
            cnt = 0;
            dfs(last,1,i - 1);
            ans = (ans * cnt) % mod;
            last = i;
            lass = s;
            num = 0;
        }
        int x = s.find(edges[i].u),y = s.find(edges[i].v);
        if(x == y)continue;
        num++;
        s.merge(x,y);
    }
    printf("%d\n",s.sz == 1 ? ans : 0);
    return 0;
}

相关文章:

  • 2021-09-23
  • 2021-09-13
  • 2021-11-19
  • 2021-07-06
  • 2021-11-28
  • 2021-12-02
猜你喜欢
  • 2021-05-03
  • 2021-12-02
相关资源
相似解决方案