zoukankan      html  css  js  c++  java
  • Wannafly Camp 2020 Day 1D 生成树

    给出两幅 (n(leq 400)) 个点的无向图 (G_1 ,G_2),对于 (G_1) 的每一颗生成树,它的权值定义为有多少条边在 (G_2) 中出现。求 (G_1) 所有生成树的权值和。

    Solution

    很容易想到,设 (G_1) 中每条边的权值,这条边在 (G_2) 中出现则权值为 (1),否则权值为 (0)

    现在就真的是求所有生成树的边权和的权值和了。

    然而标准的 Matrix-Tree Theorem 求的是生成树的边权积的和。

    现在我们定义每条只出现在 (G_1) 中的边边权为 (1),同时出现在 (G_1,G_2) 中的边权为 (x),则基尔霍夫矩阵的每个元素嗾使一个多项式,记为 (B(x))

    (det B(x)) 是一个 (n-1) 次多项式 (f(x) = sum a_i x^i),那么其中 (a_i) 就是使用了 (i) 条公共变的生成树个数。

    于是答案就是 (f'(1)=sum ia_i=det B(1) cdot sum_i sum_j (B^{-1}(1))_{i,j}cdot B'(1)_{i,j})

    于是用 Gauss 消元法求行列式和逆矩阵即可

    #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;
    }
    
  • 相关阅读:
    微信报警提示
    使用pygal图表显示网站API接口数据
    读写文本文件,乱码解决方案
    MD5加密
    将DataTable导入到SQL数据库表中
    NPOI组件操作Excel导入、导出
    二叉树由先序和中序建树
    用两个栈模拟队列
    math type白嫖教程
    IDEA常用快捷键
  • 原文地址:https://www.cnblogs.com/mollnn/p/12334957.html
Copyright © 2011-2022 走看看