Wannaflyキャンプ2020デイ1Dスパニングツリー - 行列木定理、ガウス消去

得られる2 \(N(\当量400) \) 図ない点\(G_1、G_2 \の)、のための\(G_1 \)スパニングツリーそれぞれ、その重量の数のエッジで\(G_2 \)が表示されます。探している\(G_1 \)の重みと、すべてにまたがります。

解決

聞かせて、想像するのは簡単です\(G_1 \)各エッジの重み、この辺\(G_2 \)の右側の値で表示されますが(1 \)\のそれ以外の場合は右の値は、(\ 0)\

今では本当にスパニングツリーの右側を追求し、すべての重みとされます。

しかし、標準のマトリックスツリー定理は、エッジの重みの積を求めて、スパニングされます。

今、私たちは、各のみ発生定義(G_1 \)\コラージュは右にされる(1 \)\、もに表示されます(G_1、G_2 \)\側が右側にある(X- \)\智Erhuo、その後、マトリックス総士の各要素多項式カーディフは、と呼ばれる(\ B(X))\

\(\ DET B(X) \) \(N-1 \)次多項式\(a_iをX ^ I \ F(X)= \ SUM) 次いで、ここで\(a_iを\)は、の使用である\(I \)共通スパニングツリーの変更の数。

答えがあるので\(F '(1)= \和ia_i = \ DET B(1)\ CDOT \ sum_i \ sum_j(B ^ { - 1}(1))_ {I、J} \ CDOT B'(1 )_ {I、J} \ )

このような決定要因と逆行列ガウス消去法を用いることができます

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 405;
const int mod = 998244353;

namespace mat {
int f[N][N<<1],a[N][N],n;
inline void exgcd(int a,int b,int &x,int &y) {
    if(!b) {
        x=1,y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    int t=x;
    x=y,y=t-(a/b)*y;
}

inline int inv(int a,int b) {
    int x,y;
    return exgcd(a,b,x,y),(x%b+b)%b;
}
int getdet() {
    int det=1;
    int flag=0;
    for(int i=1; i<=n; i++) {
        for(int j=i+1; j<=n; j++) {
            int x=i,y=j;
            while(a[y][i]!=0) {
                int t=a[x][i]*inv(a[y][i],mod)%mod;
                for(int k=i; k<=n; k++) (a[x][k]-=t*a[y][k]%mod)%=mod;
                swap(x,y);
            }
            if(x!=i) {
                for(int k=1; k<=n; k++) {
                    swap(a[x][k],a[i][k]);
                }
                flag^=1;
            }
        }
        if(a[i][i]==0)  return 0;
        det=det*a[i][i]%mod;
    }
    if(flag) det=-det;
    det%=mod; det+=mod; det%=mod;
    return det;
}

int solve() {
    int m=n*2;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) a[i][j]=f[i][j], f[i][j+n]=0;
    }
    int ret= getdet();
    for(int i=1; i<=n; ++i) {
        f[i][n+i]=1;
    }
    for(int i=1; i<=n; ++i) {
        for(int j=i; j<=n; j++)
            if(f[j][i]) {
                for(int k=1; k<=m; k++)
                    swap(f[i][k],f[j][k]);
                break;
            }
        if(!f[i][i]) {
            return 0;
        }
        int r=inv(f[i][i],mod);
        for(int j=i; j<=m; ++j)
            f[i][j]=f[i][j]*r%mod;
        for(int j=1; j<=n; ++j)
            if(j!=i) {
                r=f[j][i];
                for(int k=i; k<=m; ++k)
                    f[j][k]=(f[j][k]-r*f[i][k]%mod+mod)%mod;
            }
    }
    return ret;
}
}

int n,b[N][N],bd[N][N];
char g1[N][N],g2[N][N];

signed main() {
    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>g1[i]+1;
    }
    for(int i=1;i<=n;i++) {
        cin>>g2[i]+1;
    }
    for(int i=1;i<=n;i++) {
        for(int j=1;j<i;j++) {
            if(g1[i][j]=='1') b[i][j]=-1, b[j][i]=-1, b[i][i]++, b[j][j]++;
            if(g1[i][j]=='1' && g2[i][j]=='1') bd[i][j]=-1, bd[j][i]=-1, bd[i][i]++, bd[j][j]++;
        }
    }
    for(int i=1;i<n;i++) {
        for(int j=1;j<n;j++) {
            mat::f[i][j]=b[i][j];
        }
    }
    --n;
    mat::n=n;
    int det=mat::solve();
    int ans=0;
    /*for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) cout<<b[i][j]<<" ";
        cout<<endl;
    }
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) cout<<bd[i][j]<<" ";
        cout<<endl;
    }
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) cout<<mat::f[i][j+n]<<" ";
        cout<<endl;
    }*/
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) {
            ans+=mat::f[i][j+n]*bd[i][j];
            ans%=mod;
            ans+=mod;
            ans%=mod;
        }
    }
    //cout<<ans<<" "<<det<<endl;
    cout<<((ans*det)%mod+mod)%mod;
}

おすすめ

転載: www.cnblogs.com/mollnn/p/12334957.html