【JSOI2008】【BZOJ1016】最小生成树计数

我就爱写矩阵树定理!!!

就不写暴力!!!

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

Time Limit: 1 Sec Memory Limit: 162 MB

Submit: 3584 Solved: 1429

[Submit][Status][Discuss]

Description

现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。

Input

第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。

Output

输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。

Sample Input

4 6

1 2 1

1 3 1

1 4 1

2 3 2

2 4 1

3 4 1

Sample Output

8

HINT

Source

边权把所有边排序

边权相同的边构成连通块

每个连通块做一遍矩阵树

答案相乘

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define P 31011
#define MAXN 110
#define MAXINT 0x7fffffff
using namespace std;
int C[MAXN][MAXN],D[MAXN][MAXN],A[MAXN][MAXN];
int block[MAXN][MAXN],top[MAXN];
long long ans=1;
int n,m;
bool vis[MAXN];
struct Set
{
    int f[MAXN];
    int find(int x)
    {
        if (f[x]==x)    return x;
        return f[x]=find(f[x]);
    }
    void Union(int x,int y)
    {
        int a=find(x),b=find(y);
        f[b]=a;
    }
}s1,s2;//对每个连通块用s2,总体用s1
struct edge
{
    int u,v,w;
    bool operator <(const edge& a)const
    {
        return w<a.w;
    }
}e[MAXN*10];
void in(int &x)
{
    char ch=getchar();x=0;
    while (!(ch>=‘0‘&&ch<=‘9‘)) ch=getchar();
    while (ch>=‘0‘&&ch<=‘9‘)    x=x*10+ch-‘0‘,ch=getchar();
}
int calc(int size)
{
    int ret=1;
    for (int i=1;i<=size;i++)
        for (int j=1;j<=size;j++)
            C[i][j]=(C[i][j]+P)%P;
    for (int i=1;i<=size;i++)
    {
        for (int j=i+1;j<=size;j++)
        {
            int a=C[i][i],b=C[j][i];
            while (b)
            {
                int t=a/b;a%=b;swap(a,b);
                for (int k=i;k<=size;k++)   C[i][k]=(C[i][k]-C[j][k]*t)%P;
                for (int k=i;k<=size;k++)   swap(C[i][k],C[j][k]);
                ret=-ret;
            }
        }
        if (!C[i][i])   return 0;
        ret*=C[i][i];ret%=P;
    }
    return (ret+P)%P;
}
void print()
{
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)  cout<<C[i][j]<<‘ ‘;
        cout<<endl;
    }
}
void Print()
{
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)  cout<<A[i][j]<<‘ ‘;
        cout<<endl;
    }
    cout<<endl;
}
int main()
{
    in(n);in(m);
    if (m<n-1)
    {
        cout<<0<<endl;
        return 0;
    }
    for (int i=1;i<=m;i++)  in(e[i].u),in(e[i].v),in(e[i].w);
    for (int i=1;i<=n;i++)  s1.f[i]=s2.f[i]=i;
    sort(e+1,e+m+1);
    int t=-MAXINT;
    for (int i=1;i<=m+1;i++)
    {
        if (e[i].w!=t||i>m)//边权不同,计算行列式值,进入下一个连通块
        {
            for (int j=1;j<=n;j++)
                if (vis[j])
                {
                    int F=s2.find(j);
                    block[F][++top[F]]=j;
                    vis[j]=0;
                }
            for (int j=1;j<=n;j++)
                if (top[j]>1)
                {
                    memset(C,0,sizeof(C));
                    for (int k=1;k<=top[j];k++)//对连通块构建矩阵
                        for (int l=k+1;l<=top[j];l++)
                        {
                            int a=block[j][k],b=block[j][l];
                            C[k][l]=(C[l][k]-=A[a][b]);
                            C[k][k]+=A[a][b];C[l][l]+=A[a][b];
                        }
                    ans=(ans%P*calc(top[j]-1))%P;
                    for (int k=1;k<=top[j];k++) s1.f[block[j][k]]=j;
                }
            for (int j=1;j<=n;j++)
            {
                s1.f[j]=s2.f[j]=s1.find(j);
                top[j]=0;memset(block[j],0,sizeof(block[j]));
            }
            if (i>m)    break;
            t=e[i].w;
        }
        int x=s1.find(e[i].u),y=s1.find(e[i].v);
        if (x==y)   continue;
        vis[x]=vis[y]=1;
        s2.Union(x,y);
        D[x][x]++;D[y][y]++;A[x][y]++;A[y][x]++;
    }
    cout<<ans<<endl;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 04:16:04

【JSOI2008】【BZOJ1016】最小生成树计数的相关文章

[JSOI2008][BZOJ1016] 最小生成树计数

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3379  Solved: 1336[Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的

BZOJ_1016_[JSOI2008]_最小生成树计数_(dfs+乘法原理)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1016 给出一张图,其中具有相同权值的边的数目不超过10,求最小生成树的个数. 分析 生成树的计数有一个什么什么算法... 我真的企图研究了...但是智商捉急的我实在看不懂论文... 所以最后还是写了暴力... 当然暴力也要靠正确的姿势的. 首先来看一个结论: 同一张图的所有最小生成树中,边权值相同的边的数目是一定的. 也就是说,假如某一张图的某一棵最小生成树由边权值为1,1,2,2,2,3的

【BZOJ】【1016】【JSOI2008】最小生成树计数

Kruskal/并查集+枚举 唉我还是too naive,orz Hzwer 一开始我是想:最小生成树删掉一条边,再加上一条边仍是最小生成树,那么这两条边权值必须相等,但我也可以去掉两条权值为1和3的,再加上权值为2和2的,不也满足题意吗?事实上,如果这样的话……最小生成树应该是1和2,而不是1和3或2和2!!! 所以呢?所以对于一个图来说,最小生成树有几条边权为多少的边,都是固定的!所以我们可以做一遍Kruskal找出这些边权,以及每种边权出现的次数.然后,对于每种边权,比方说出现了$v_i$

Bzoj1016 最小生成树计数

Time Limit: 1000MS   Memory Limit: 165888KB   64bit IO Format: %lld & %llu Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了. Input 第一行包含两个数,n和m,其中1<=n<=1

【bzoj1016】 JSOI2008—最小生成树计数

http://www.lydsy.com/JudgeOnline/problem.php?id=1016 (题目链接) 题意:求图的最小生成树计数. Solution  %了下题解,发现要写矩阵树,150++的程序什么鬼.于是就蒯了hzwer的简便方法.  将边按照权值大小排序,将权值相同的边分到一组,统计下每组分别用了多少条边.然后对于每一组进行dfs,判断是否能够用这一组中的其他边达到相同的效果.最后把每一组的方案数相乘就是答案.  注意并查集不要压缩路径,不然的话不好回溯. 代码: //

bzoj1016: [JSOI2008]最小生成树计数(kruskal+dfs)

1016: [JSOI2008]最小生成树计数 题目:传送门 题解: 神题神题%%% 据说最小生成树有两个神奇的定理: 1.权值相等的边在不同方案数中边数相等  就是说如果一种方案中权值为1的边有n条    那么在另一种方案中权值为1的边也一定有n条 2.如果边权为1的边连接的点是x1,x2,x3   那么另一种方案中边权为1的边连接的也一定是x1,x2,x3  如果知道了这两条定理那就很好做了啊: 因为等权边的条数一定,那么我们就可以预处理求出不同边权的边的条数 题目很人道的保证了边权相同的边

JSOI2008 最小生成树计数

题解: 最小生成树的两个性质: 1.边权相等的边的个数一定. 2.做完边权为w的所有边时,图的连通性相同. 证明: 1.边权相等的边的个数不一样的话就不会都同时是最小生成树了. 2.假设每种方法的做完边权为w的连通性不同,那么假设i边和j边没有同时被选,那么我们完全可以在一种方案中加入i边(或j边),使得连通性增强,而后面费用更大的边用的更少,这样与这是最小生成树矛盾.于是,命题得证. 代码:不知为何,下面程序有bug,什么时候再回来A掉…… 1 type node1=record 2 x,y,

BZOJ 题目1016: [JSOI2008]最小生成树计数(Kruskal+Matrix_Tree)

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 3569  Solved: 1425 [Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对3101

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

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 6200  Solved: 2518[Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的 最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生 成树可能很多,所以你只需要输出方案数对3101