kuangbin专题八生成树总结

总结:生成树的知识点真多,不过博主觉得图论的题目终究是建图难,第一步就是如何建图,将其转换成已知的问题。
另外,关于生成树的两个注意点,也是为了防止碰到毒瘤题。一就是自环,二就是重边。
A - The Unique MST
次小生成树裸题。
prim模板

#include
#include
#include
#include
using namespace std;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int map[maxn][maxn];

int lowcost[maxn];
int closest[maxn];
bool used[maxn][maxn];
bool vis[maxn];
int Max[maxn][maxn];
int n, m;
int prim()
{
    memset(lowcost, inf, sizeof(lowcost));
    memset(closest, 0, sizeof(closest));
    memset(used, 0, sizeof(used));
    memset(vis, 0, sizeof(vis));
    memset(Max, 0, sizeof(Max));
    lowcost[1] = 0;
    vis[1] = 1;
    closest[1] = 1;
    int num = 0, ans = 0;
    int nownode = 1;
    while (num1)
    {
        int Mincost = inf, theidx;
        for (int i = 1;i <= n;i++)
        {
            if (vis[i])
            {
                if (i == nownode)continue;
                Max[i][nownode] = Max[nownode][i] = max(Max[i][closest[nownode]], lowcost[nownode]);
            }
            else
            {
                if (lowcost[i] > map[i][nownode])
                {
                    lowcost[i] = map[i][nownode];
                    closest[i] = nownode;
                }
                if (Mincost > lowcost[i])
                {
                    Mincost = lowcost[i];
                    theidx = i;
                }
            }
        }
        ans += Mincost;
        num++;
        nownode = theidx;
        vis[nownode] = 1;
        used[theidx][closest[theidx]] = used[closest[theidx]][theidx] = 1;
    }
    for (int i = 1;i <= n;i++)
        Max[i][nownode] = Max[nownode][i] = max(Max[i][closest[nownode]], lowcost[nownode]);
    return ans;
}

void solve(int num)
{
    for (int i = 1;i <= n;i++)
        for (int j = i + 1;j <= n;j++)if (!used[i][j])
        {
            int ans = num - Max[i][j] + map[i][j];
            if (ans == num)
            {
                puts("Not Unique!");
                return;
            }
        }
    printf("%d\n", num);
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {

        scanf("%d %d", &n, &m);
        int u, v, dis;
        memset(map, inf, sizeof(map));
        for (int i = 1;i <= m;i++)
        {
            scanf("%d %d %d", &u, &v, &dis);
            map[u][v] = map[v][u] = dis;
        }
        int ans = prim();
        solve(ans);
    }
}

D - Is There A Second Way Left?
这题相对于上题来说,有重边。所以用kruskal(个人感觉也能用prim)。。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
const int maxn = 105;
const int maxm = 205;
const int inf = 0x3f3f3f3f;

struct Edge
{
    int u, v, dis;
    bool vis;

    Edge(int _u, int _v, int _d) :u(_u), v(_v), dis(_d) { vis = 0; }
    Edge(){}
    bool operator<(const Edge &b)const
    {
        return dis < b.dis;
    }
}edges[maxm];
int tot;
int n, m;
vector<int>node[maxn];
void addedge(int u, int v, int dis)
{
    edges[tot++] = Edge(u, v, dis);
}

int f[maxn];
int find(int a)
{ 
    return a == f[a] ? a : f[a] = find(f[a]); 
}
int Max[maxn][maxn];
int Kruskal()
{
    sort(edges + 1, edges + tot);
    for (int i = 1;i <= n;i++)
        f[i] = i, node[i].clear(), node[i].push_back(i);
    memset(Max, 0, sizeof(Max));
    int ans = 0, tot1 = 0;
    for (int i = 1;i < tot;i++)
    {
        if (tot1 == n - 1)break;
        int u = edges[i].u, v = edges[i].v, dis = edges[i].dis;
        int fu = find(u), fv = find(v);
        if (fu != fv)
        {
            edges[i].vis = 1;
            tot1++;
            ans += dis;
            f[fv] = fu;
            //cout << find(fu) << endl;
            int Sizefu = node[fu].size();
            int Sizefv = node[fv].size();
            for (int j = 0;j < Sizefu;j++)
                for (int k = 0;k < Sizefv;k++)
                    Max[node[fu][j]][node[fv][k]] = Max[node[fv][k]][node[fu][j]] = dis;
            int tmp[105];
            for (int j = 0;j < Sizefu;j++)
                tmp[j] = node[fu][j];
            for (int j = 0;j < Sizefv;j++)
                node[fu].push_back(node[fv][j]);
            for (int j = 0;j < Sizefu;j++)
                node[fv].push_back(tmp[j]);
        }
    }
    if (tot1 != n - 1)return -1;
    return ans;
}

int solve(int num)
{
    int ans = inf;
    for (int i = 1;i if (!edges[i].vis)
            ans = min(ans, num - Max[edges[i].u][edges[i].v] + edges[i].dis);
    if (ans == inf)
        return -1;
    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    int cas = 1;
    while (T--)
    {
        tot = 1;

        scanf("%d %d", &n, &m);
        int u, v, dis;
        for (int i = 1;i <= m;i++)
        {
            scanf("%d %d %d", &u, &v, &dis);
            addedge(u, v, dis);
        }
        printf("Case #%d : ", cas++);
        int ans1 = Kruskal();
        if (ans1 == -1)
            puts("No way");
        else
        {
            int ans2 = solve(ans1);
            if (ans2 == -1)
                puts("No second way");
            else
                printf("%d\n", ans2);
        }
    }
}

另外一种写法就是先跑kruskal,然后删掉一个边(使边两点并查集一样即可),然后再跑kruskal。这里就不写了。。

接下来就是最小树形图问题,也就是有向图的最小生成树。这里就是朱刘算法的使用。
这里有2个问题需要说明。
1,朱刘算法的模板有两份,一份是矩阵存图,一种是存边。有的时候用其中一份会超时,这时候就得自行判断一下用哪个了。例如下面的G。
2,有向图的最小生成树,有的时候会指定根,那么这个时候直接套就行了。如果没有指定根的话,叫你输出根,这时候就需要建图方法了。首先先记录所有边权和为sum,然后建一个虚点,虚点连向所有点,边权为sum+1,然后跑朱刘算法,如果跑出来的ans>=2*sum+2,就说明图不连通。如何求出的根呢,假设原有边有m条,虚点为s,我们在选边(假设边下标为num)的时候,如果选择到的边有一个点是s的话,那么更新root=num-m。这个也比较好理解。
F - Teen Girl Squad
矩阵存图版本

#include
#include
#include
using namespace std;

const int maxn = 1005;
const int INF = 1e9;
bool vis[maxn];
bool flag[maxn];
int w[maxn][maxn];
int pre[maxn];
int n, m;
void init()//不能少了初始化的内容  
{
    memset(vis, 0, sizeof(vis));
    memset(flag, 0, sizeof(flag));
    for (int i = 0; i <= n; i++)
    {
        w[i][i] = INF;
        for (int j = i + 1; j <= n; j++)
            w[i][j] = w[j][i] = INF;
    }
}

int directed_mst(int u)//u表示根节点  
{
    int ans = 0;
    memset(vis, 0, sizeof(vis));
    while (true)
    {
        //求最短弧集合E  
        for (int i = 1; i <= n; i++)if (i != u && !flag[i])
        {
            w[i][i] = INF, pre[i] = i;
            for (int j = 1; j <= n; j++)if (!flag[j] && w[j][i]if (pre[i] == i)return -1;//也可以用dfs预处理判断凸的连通  
        }
        //判断E是否有环  
        int i;
        for (i = 1; i <= n; i++)
        {
            if (i != u && !flag[i])
            {
                int j = i, cnt = 0;
                while (j != u && pre[j] != i && cnt <= n) j = pre[j], ++cnt;
                if (j == u || cnt>n) continue; //最后能找到起点(根)或者是走过的点已经超过了n个,表示没有有向环  
                break;
            }
        }
        if (i>n)
        {
            for (int i = 1; i <= n; i++)if (i != u && !flag[i]) ans += w[pre[i]][i];
            return ans;
        }
        //有环,进行收缩,把整个环都收缩到一个点i上。  
        int j = i;
        memset(vis, 0, sizeof(vis));
        do
        {
            ans += w[pre[j]][j], j = pre[j], vis[j] = flag[j] = true;//对环内的点标记,并且直接对环的权值进行加和记录,在最后找到最小树形图之后就不用展开收缩点了  
        } while (j != i);
        flag[i] = false; // 环缩成了点i,点i仍然存在  

                         //收缩点的同时,对边权值进行改变  
        for (int k = 1; k <= n; ++k)if (vis[k])  // 在环中点点  
        {
            for (int j = 1; j <= n; j++)if (!vis[j])   // 不在环中的点  
            {
                if (w[i][j] > w[k][j]) w[i][j] = w[k][j];
                if (w[j][k]return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    int cas = 1;
    while (T--)
    {
        scanf("%d %d", &n, &m);
        init();
        int u, v,dis;
        for (int i = 1;i <= m;i++)
        {
            scanf("%d %d %d", &u, &v, &dis);
            w[u+1][v+1] = min(w[u+1][v+1],dis);
        }
        printf("Case #%d: ", cas++);
        int ans = directed_mst(1);
        if (ans == -1)
            puts("Possums!");
        else
            printf("%d\n", ans);
    }
    return 0;
}

G - Ice_cream’s world II
有的时候用矩阵的会超时,那么这里就用存边版本的。

#include   
#include   
#include   
using namespace std;  
#define N 1010  
#define INF 0x7f7f7f7f  
struct Edge  
{  
    int u,v,w;  
} e[N*N];  
int cnt;  
int in[N];  
int vis[N],pre[N],id[N];  
int minroot;  
void addedge(int u,int v,int w)  
{  
    e[cnt].u=u;  
    e[cnt].v=v;  
    e[cnt++].w=w;  
}  
int Directed_MST(int root,int NV,int NE)  
{  
    int ret = 0;  
    while(true)  
    {  
        ///步骤1:找到最小边  
        for(int i = 0; i < NV; i ++)  
            in[i] = INF;  
            memset(pre,-1,sizeof(pre));  
        for(int i = 0; i < NE; i ++)  
        {  
            int u = e[i].u , v = e[i].v;  
            if(e[i].w < in[v] && u != v)  
            {  
                pre[v] = u;  
                in[v] = e[i].w;  
                if(u==root) minroot=i;  
            }  
        }  
        for(int i = 0; i < NV; i ++)  
        {  
            if(i == root) continue;  
            if(in[i] == INF) return -1;///除了根节点以外有点没有入边,则根无法到达他  
        }  
        int cntnode = 0;  
        memset(id,-1,sizeof(id));  
        memset(vis,-1,sizeof(vis));  
        ///找环  
        in[root] = 0;  
        for(int i = 0; i < NV; i ++) ///标记每个环,编号  
        {  
            ret += in[i];  
            int v = i;  
            while(vis[v] != i && id[v] == -1 && v != root)  
            {  
                vis[v] = i;  
                v = pre[v];  
            }  
            if(v != root && id[v] == -1)  
            {  
                for(int u = pre[v]; u != v; u = pre[u])  
                {  
                    id[u] = cntnode;  
                }  
                id[v] = cntnode ++;  
            }  
        }  
        if(cntnode == 0) break;//无环  
        for(int i = 0; i < NV; i ++)  
            if(id[i] == -1)  
                id[i] = cntnode ++;  
        ///步骤3:缩点,重新标记  
        for(int i = 0; i < NE; i ++)  
        {  
            int u=e[i].u;  
            int v = e[i].v;  
            e[i].u = id[u];  
            e[i].v = id[v];  
            if(e[i].u != e[i].v) e[i].w -= in[v];  
        }  
        NV = cntnode;  
        root = id[root];  
    }  
    return ret;///最小树形图的长度  
}  

int main()  
{  
    int n,m,sum;  
    int u,v,w;  
    while(scanf("%d %d",&n,&m)!=EOF)  
    {  
        cnt=0;sum=0;  
        for(int i=0; i"%d %d %d",&u,&v,&w);  
            addedge(u+1,v+1,w);  
            sum+=w;  
        }  
        sum++;  
        for(int i=1; i<=n; i++)  
            addedge(0,i,sum);  
        int ans=Directed_MST(0,n+1,cnt);  
        if(ans==-1||ans>=2*sum)  
            printf("impossible\n\n");  
        else  
            printf("%d %d\n\n",ans-sum,minroot-m);  
    }  
    return 0;  
}  

接下来就是生成树计数问题,做这个问题,首先你得要有一个矩阵模板。
这里说明一下矩阵的一个问题。
有的时候需要模一个数,那么矩阵求行列式就只能用辗转相除法,否则就能用高精度。
生成树计数目前碰到的一个问题,就是限制边权最小的生成树计数,也就是最小生成树计数,这个有点难,稍后会解释。
L - Lightning
随便贴一个题来展示博主的模板。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps = 1e-10;
const int maxn = 305;
const int mod = 10007;
int sgn(ld x)
{
    if (fabs(x) < eps)return 0;
    else if (x > 0)return 1;
    return -1;
}

struct M
{
    int n, m;
    ll a[maxn][maxn];
    M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }

    void pri()
    {
        for (int i = 1;i <= n;i++)
        {
            for (int j = 1;j <= m;j++)cout << a[i][j] << ' ';
            cout << endl;
        }
    }
    friend M operator*(M a, M b)
    {
        M c;
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    c.a[i][j] += a.a[i][k] * b.a[k][j];
        return c;
    }
    friend M operator-(M a, M b)
    {
        for (int i = 1;i <= a.n;i++)
            for (int j = 1;j <= a.m;j++)
                a.a[i][j] -= b.a[i][j];
        return a;
    }

    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 1;
    }

    //行列式高精度
    long double mat[maxn][maxn], tmp[maxn];
    long double det()
    {
        long double ans = 1;
        for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) mat[i][j] = a[i][j];
        for (int i = 1;i <= n;i++)
        {
            int pos = i;
            while (fabs(mat[pos][i])if (fabs(mat[pos][i])return 0;
            if (pos^i)
            {
                copy(mat[pos] + 1, mat[pos] + 1 + m + 1, tmp + 1);
                copy(mat[i] + 1, mat[i] + 1 + m + 1, mat[pos] + 1);
                copy(tmp + 1, tmp + 1 + m + 1, mat[i] + 1);
            }
            ans *= mat[i][i];
            for (int j = i + 1;j <= n;j++)
            {
                long double p = mat[j][i] / mat[i][i];
                for (int k = i;k <= m;k++) mat[j][k] -= mat[i][k] * p;
            }
        }
        return ans;
    }

    //行列式辗转相除法
    ll det(ll mod)
    {
        ll ret = 1;
        for (int i = 1;i <= n;i++)
        {
            if (a[i][i] < 0)
            {
                ret = -ret;
                for (int k = i;k <= n;k++)a[i][k] = -a[i][k];
            }
            for (int j = i + 1;j <= n;j++)
            {
                for (int k = i;k <= n;k++)a[i][k] %= mod, a[j][k] %= mod;
                while (a[j][i])
                {
                    if (a[j][i] < 0)
                    {
                        ret = -ret;
                        for (int k = i;k <= n;k++)a[j][k] = -a[j][k];
                    }
                    ll t = a[i][i] / a[j][i];
                    for (int k = i;k <= n;k++)a[i][k] = (a[i][k] - t*a[j][k]) % mod;
                    for (int k = i;k <= n;k++)swap(a[i][k], a[j][k]);
                    ret = -ret;
                }
            }
            if (a[i][i] == 0)return 0;
            ret = ret*a[i][i] % mod;
        }
        return (ret + mod) % mod;
    }
}A, C, D;

struct node
{
    int x, y;
    node(int _x,int _y):x(_x),y(_y){}
    node(){}
}nodes[maxn];


double map[maxn][maxn];
int N, R;
double dist(int a, int b)
{
    double x = nodes[a].x - nodes[b].x;
    double y = nodes[a].y - nodes[b].y;
    return sqrt(x*x + y*y);
}
vector<int>V[maxn];
bool check(int a, int b)
{
    for(auto it:V[a])if(it!=b)
        if (!sgn(map[a][it] + map[it][b] - map[a][b]))
            return 0;
    return 1;
}



int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {

        scanf("%d %d", &N, &R);
        for (int i = 1;i <= N;i++)V[i].clear();
        A.mem(N);
        C.mem(N);
        D.mem(N);
        int x, y;
        for (int i = 1;i <= N;i++)
        {
            scanf("%d %d", &x, &y);
            nodes[i] = node(x, y);
        }
        for(int i=1;i<=N;i++)
            for (int j = i+1;j <= N;j++)if (i != j)
            {
                double tmp = dist(i, j);
                map[i][j] = map[j][i] = tmp;
                if (tmp <= R)
                    V[i].push_back(j), V[j].push_back(i);
            }

        for(int i=1;i<=N;i++)
            for (int j = i + 1;j <= N;j++)if (map[i][j] <= R)
            {
                if (check(i, j))
                {
                    A.a[i][j] = A.a[j][i] = 1;
                    D.a[i][i]++;
                    D.a[j][j]++;
                }
            }

        for (int i = 1;i <= N;i++)
            for (int j = 1;j <= N;j++)
                C.a[i][j] = D.a[i][j] - A.a[i][j];
        //C.pri();
        C.n--;C.m--;
        int ans = C.det(mod);
        printf("%d\n", ans == 0 ? -1 : ans);

    }

}

M - Minimum Spanning Tree
如何计算最小生成树个数呢?
首先我们回顾一下kruskal算法。
kruskal算法首先将边按照边权排序,假设边权先有一堆c1,然后c2,然后c3…(c1< c2< c3).
我们首先会算c1的结果,而计算c2时,前面的结果实际和现在算c2是互不干扰的。也就是说,我们在计算最小生成树时,可以把它分成若干个阶段。在算c1阶段时,我们可以算它的生成树个数,然后再算c2时,再算它的生成树个数,然后相乘就是答案了。
知道思路了,再说一个比较混的点。
设fa[maxn]为目前的连通状态,ka[maxn]为更新后的连通状态。在找边的时候,不能更新fa,而只能更新ka,在计算完生成树后,再更新fa。

#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef long double ld;
const ld eps = 1e-10;
const int maxn = 105;
const int maxm = 1005;
const int mod = 1e9;
int sgn(ld x)
{
    if (fabs(x) < eps)return 0;
    else if (x > 0)return 1;
    return -1;
}

struct M
{
    int n, m;
    ll a[maxn][maxn];
    M(int _n = 0) { n = m = _n;memset(a, 0, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, 0, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, 0, sizeof(a)); }

    void pri()
    {
        for (int i = 1;i <= n;i++)
        {
            for (int j = 1;j <= m;j++)cout << a[i][j] << ' ';
            cout << endl;
        }
    }
    friend M operator*(M a, M b)
    {
        M c;
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    c.a[i][j] += a.a[i][k] * b.a[k][j];
        return c;
    }
    friend M operator-(M a, M b)
    {
        for (int i = 1;i <= a.n;i++)
            for (int j = 1;j <= a.m;j++)
                a.a[i][j] -= b.a[i][j];
        return a;
    }

    void make_I(int _n)
    {
        n = m = _n;
        memset(a, 0, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 1;
    }

    //行列式高精度
    long double mat[maxn][maxn], tmp[maxn];
    long double det()
    {
        long double ans = 1;
        for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) mat[i][j] = a[i][j];
        for (int i = 1;i <= n;i++)
        {
            int pos = i;
            while (fabs(mat[pos][i])if (fabs(mat[pos][i])return 0;
            if (pos^i)
            {
                copy(mat[pos] + 1, mat[pos] + 1 + m + 1, tmp + 1);
                copy(mat[i] + 1, mat[i] + 1 + m + 1, mat[pos] + 1);
                copy(tmp + 1, tmp + 1 + m + 1, mat[i] + 1);
            }
            ans *= mat[i][i];
            for (int j = i + 1;j <= n;j++)
            {
                long double p = mat[j][i] / mat[i][i];
                for (int k = i;k <= m;k++) mat[j][k] -= mat[i][k] * p;
            }
        }
        return ans;
    }

    //行列式辗转相除法
    ll det(ll mod)
    {
        ll ret = 1;
        for (int i = 1;i <= n;i++)
        {
            if (a[i][i] < 0)
            {
                ret = -ret;
                for (int k = i;k <= n;k++)a[i][k] = -a[i][k];
            }
            for (int j = i + 1;j <= n;j++)
            {
                for (int k = i;k <= n;k++)a[i][k] %= mod, a[j][k] %= mod;
                while (a[j][i])
                {
                    if (a[j][i] < 0)
                    {
                        ret = -ret;
                        for (int k = i;k <= n;k++)a[j][k] = -a[j][k];
                    }
                    ll t = a[i][i] / a[j][i];
                    for (int k = i;k <= n;k++)a[i][k] = (a[i][k] - t*a[j][k]) % mod;
                    for (int k = i;k <= n;k++)swap(a[i][k], a[j][k]);
                    ret = -ret;
                }
            }
            if (a[i][i] == 0)return 0;
            ret = ret*a[i][i] % mod;
        }
        return (ret + mod) % mod;
    }
}A, C;

struct Edge
{
    int u, v, dist;
    Edge(int _u, int _v, int _d) :u(_u), v(_v), dist(_d) {}
    Edge() {}
    bool operator<(const Edge &b)const
    {
        return dist < b.dist;
    }
}edges[maxm];
int n, m, p;

int find(int a, int *f) { return a == f[a] ? a : f[a] = find(f[a], f); }
int f[maxn], ka[maxn];
bool vis[maxn];
vector<int>V[maxn];
ll matrix_tree()
{
    for (int i = 1;i <= n;i++)if (vis[i])
        V[find(i, ka)].push_back(i);
    memset(vis, 0, sizeof(vis));
    ll ans = 1;
    for (int i = 1;i <= n;i++)if (V[i].size())
    {
        int Size = V[i].size();
        C.mem(Size);
        for (int j = 0;jfor (int k = j + 1;k < Size;k++)
            {
                int u = V[i][j], v = V[i][k];
                C.a[j + 1][k + 1] = 0 - A.a[u][v];
                C.a[k + 1][j + 1] = 0 - A.a[v][u];
                C.a[j + 1][j + 1] += A.a[u][v];
                C.a[k + 1][k + 1] += A.a[u][v];
            }
        C.n--, C.m--;
        ans = ans*C.det(p) % p;
        V[i].clear();
    }

    for (int i = 1;i <= n;i++)
        f[find(i, f)] = find(i, ka);
    return ans;
}

void solve()
{
    sort(edges + 1, edges + 1 + m);
    for (int i = 1;i <= n;i++)f[i] = ka[i] = i;
    int now = edges[1].dist;
    int u, v, fu, fv;
    ll ans = 1;
    for (int i = 1;i <= m;i++)
    {
        u = edges[i].u, v = edges[i].v;
        fu = find(u, f), fv = find(v, f);
        if (fu != fv)
        {
            ka[find(fu, ka)] = find(fv, ka);
            A.a[fu][fv]++;
            A.a[fv][fu]++;
            vis[fu] = 1, vis[fv] = 1;
        }
        if (i == m || now != edges[i + 1].dist)
        {
            ans = ans*matrix_tree() % p;
            now = edges[i + 1].dist;
        }
    }
    for (int i = 2;i <= n;i++)if (ka[i] != ka[i - 1])ans = 0;
    printf("%lld\n", ans%p);
}


int main()
{

    while (~scanf("%d %d %d", &n, &m, &p) && n)
    {
        A.mem(n);
        int u, v, dis;
        for (int i = 1;i <= m;i++)
        {
            scanf("%d %d %d", &u, &v, &dis);
            edges[i] = Edge(u, v, dis);
        }
        solve();
    }


}

这题有点毒瘤的地方是,如果我不在printf那里模p的话,竟然会wa。至今不知道原因。。
之后会补充对朱刘算法和矩阵树的理解。

你可能感兴趣的:(kuangbin专题总结)