1. 程式人生 > >kuangbin專題八生成樹總結

kuangbin專題八生成樹總結

總結:生成樹的知識點真多,不過博主覺得圖論的題目終究是建圖難,第一步就是如何建圖,將其轉換成已知的問題。
另外,關於生成樹的兩個注意點,也是為了防止碰到毒瘤題。一就是自環,二就是重邊。
A - The Unique MST
次小生成樹裸題。
prim模板

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stdio.h>
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 (num<n - 1) { 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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cmath>
#include<climits>
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 <tot;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<iostream>
#include<algorithm>
#include<cstring>
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]<w[pre[i]][i])
            {
                pre[i] = j;
            }
            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]<INF && w[j][k] - w[pre[k]][k] < w[j][i])
                    w[j][i] = w[j][k] - w[pre[k]][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;
}
#include <iostream>  
#include <cstdio>  
#include <cstring>  
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<m; i++)  
        {  
            scanf("%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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cmath>
#include<climits>
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])<eps&&pos<n) ++pos;
            if (fabs(mat[pos][i])<eps) 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<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<vector>
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])<eps&&pos<n) ++pos;
            if (fabs(mat[pos][i])<eps) 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;j<Size;j++)
            for (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專題成樹總結

總結:生成樹的知識點真多,不過博主覺得圖論的題目終究是建圖難,第一步就是如何建圖,將其轉換成已知的問題。 另外,關於生成樹的兩個注意點,也是為了防止碰到毒瘤題。一就是自環,二就是重邊。 A - The Unique MST 次小生成樹裸題。 prim模板

kuangbin帶你飛 專題 成樹

HDU 4081(次小生成樹) 題意:這題是給你n個點座標,m條邊,然後讓你連成一棵生成樹,可以把其中一條邊的權值變為0,然後求這條邊兩端點的權值之和/(生成樹權值-這條邊權)最大值 題解:這得列舉刪除每一條邊啊,然後求含有這條邊的最小生成樹 看了題解之後才明白這是求次小生

[kuangbin帶你飛]專題 成樹 題解彙總

本來準備省賽和其他的比賽先不打算做kuangbin專題了,不過我朋友AK了最小生成樹專題,問我繼續怎麼練。既然都練了最小生成樹,不如把後面的那個生成樹專題也啃了吧。然後clone生成樹專題,花了5天懟完。主要是中間一堆作業要懟,耽誤了很多時間。我把我的題解和程式碼都放到了Gi

kuangbin專題 UVA10766 (成樹計數)Organising the Organisation(請無視這篇文章)

題意: 給出n,m,k,代表一家公司有n個部門,編號1到n,有m組關係,表示i和j不能直接聯通,k代表主管部門,問你有多少種分層方案。另外,這道題的k可以忽略掉,所以他的範圍完全是嚇唬人的。 題解: 抱歉,這道題我真的無法弄的通俗的說出來

kuangbin專題 URAL1627 Join(成樹計數)

題意: 給出一個圖,’.’表示臥室,’*’表示儲藏間,每個格子上下左右都有一堵牆,然後需要打通一些臥室的牆(只能是相鄰房間才能打通)使得臥室之間聯通的方案數. 給每個臥室編個號,給可以打通的臥室加邊,就是裸的生成樹計數了. 題解: 打通一

stp成樹總結

mst 中斷 同時 ces 交換 信息 臨時 速度 動作 stp生成樹協議目前主要分stp、rstp、mstp三類,依次向下兼容。 1、涉及的概念:   stp:根交換(跟網橋)、根端口、指定端口、可選端口,bpdu保護、root保護、收斂慢,單樹。   rstp:根交換、

最小生成樹 kuangbin專題最後一個題

題目連結:https://cn.vjudge.net/contest/66965#problem/N 註釋:這道題需要用krustra,用prim的話可能會超時。並且在計算距離的時候要儘量減少步驟,具體注意事項在程式碼中說吧。 AC程式碼: #include<iostream&

12.6日總結 最小度限制成樹 POJ 1639(含聯通塊dfs劃分模板)

     今天看了 一道 有度數限制的最小生成樹題目,按照書上給出的思路寫的程式碼,思路的最後一步沒有實現,可能沒弄懂書上的意思。 http://poj.org/problem?id=1639。 題意:     大概是說在一張無向圖中

企業三層架構、冗餘、STP成樹協議總結

總結 1.企業三層架構 2. 冗餘(線路冗餘+裝置冗餘) 3. STP生成樹協議:IEEE802.1D,PVST+,802.1W,RSTP(rpvst),802.1S(MST) 企業三層架構(內網結構) 接入層: 常使用二層交換機,就近提供介面密度,用於使用者的接入 匯聚層(分佈層):

kuangbin專題十二基礎dp總結

做這個專題的時候感覺好迷。一度被題噁心到了。。 這題把所有不是獨立思考做出來的題貼出來吧。 A - Max Sum Plus Plus 題解:dp[i][j] 代表前i個數在必須選第i個的前提下組成j組的最大值。 那麼方程為: dp[i][j]=ma

kuangbin專題十七AC自動機總結

這個專題寫的我頭皮發麻,出現了好多小bug耗費了我好多時間,但總體看不算太難,只要把思路縷清就行了。 AC自動機的題目有兩類,一類是字串找子串個數的,另一類則是建立狀態,然後進行dp或者矩陣快速冪。 B - 病毒侵襲 這題不算太難,但有一個坑點,就是字元都

成樹計數總結 SPOJ104

可看周冬的論文《生成樹的計數及其應用》,利用Matrix-Tree定理解決生成樹計數的問題,複雜度是矩陣乘法的複雜度O(n^3)。 總結: 無向圖,允許有重邊。 四個重要矩陣A(鄰接矩陣),D(度數矩陣),C(KirchHoff矩陣,C=D-A),B(關聯矩陣,B其實是用來

STP成樹協議和VTP

primary server flash 交換機 優先級 STP#show spanning-tree bri 查看STP生成樹SW conf#spanning-tree vlan 1 priority root 修改優先級為根,可以改成數字,0為最低SW conf#spanning-tr

3950雙層交換機成樹協議

交換機 雙層 3950雙層交換機,A/B交換機分別開啟生成樹協議。spanning-tree查看端口 show spanning-tree兩臺交換機開啟生成樹協議之後廣播風暴消失。交換機A 1/1、1/2、1/24端口交換機B 1/3、1/4、1/23端口燈不再爆閃本文出自 “11921400” 博客

[POJ 2728]Desert King(0-1分數規劃/最優比率成樹

eat ice finall nec clu bool ann channels try Description David the Great has just become the king of a desert country. To win the respec

成樹保護功能

生成樹 保護功能 1、配置 BPDU 保護功能system-view stp bpdu-protection //請在有邊緣端口的設備上進行如下配置2、配置根保護功能system-viewinterface ge 0/0/1stp root-protection3、配置環路保護功能system

成樹中的5種交換機端口狀態和3種成樹協議模式

style blocking 用戶數 forward 命令 learn 用戶數據 pvst+ class 端口狀態:①關閉(disable):端口處於管理關閉狀態 即DIS②阻塞(blocking): 不能轉發用戶數據 即BLK③監聽(listening): 接口開始啟動

【中山市選2010】【BZOJ2467】成樹

online 中心 什麽是 var cti spl scrip scanf load Description 有一種圖形叫做五角形圈。一個五角形圈的中心有1個由n個頂點和n條邊組成的圈。在中心的這個n邊圈的每一條邊同一時候也是某一個五角形的一條邊,一共

貨車運輸(最大成樹+LCA)

整數 fine std 一個 ext getchar() 最小路徑 ont getch 題目描述 A 國有 n 座城市,編號從 1 到 n,城市之間有 m 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 q 輛貨車在運輸貨物, 司機們想知道每輛車在不超過車輛限重

uva10766成樹計數

als mes art 算子 技術分享 math 個數 main mat 此類題是給定一個無向圖,求所有生成樹的個數,生成樹計數要用到Matrix-Tree定理(Kirchhoff矩陣-樹定理) G的度數矩陣D[G]是一個n*n的矩陣,並且滿足:當i≠j時,dij=0;當i