正睿省选模拟附加赛3T2(JZOJ5153)(矩阵树定理,线性代数)

在这里插入图片描述


很显然的想法就是计算每条边的贡献,显然贡献就是边权 ∗ * 选这条边的方案数,方案数为总生成树方案–去掉这条边的方案数,
暴力修改矩阵复杂度 O ( m n 3 ) O(mn^3) O(mn3)

m m m 条边每次都只修改矩阵中一行的两个值,考虑优化计算去掉每条边的方案数。
假设 M M M 为代数余子矩阵,有
∣ A ∣ = ∑ i = 1 n A i , j ∗ M i , j |A|=\sum_{i=1}^n A_{i,j}*M_{i,j} A=i=1nAi,jMi,j
每次就只修改 x x x 行中的两个位置,那我们把行列式按第 x x x 展开,预处理好代数余子矩阵后每次可以 O ( 1 ) O(1) O(1) 计算行列式
复杂度 O ( n 3 + m ) O(n^3+m) O(n3+m)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define fr first
#define se second
#define mp make_pair
#define ll long long
int rd()
{
    int sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
    if(flag) return sum;
    else return -sum;
}
const int p = 1e9+7;
int n,m;
int cur,det;
int mat[310][310];
int f[310][310],g[310][310];
struct node{int l,r,v;}e[101000];
int mul(int a,int b){return 1ll*a*b%p;}
int calc(int a,int b){return (a+b)%p;}
int del(int a,int b){return ((a-b)%p+p)%p;}
int Pow(int a,int x){int now = 1;for(;x;x >>= 1,a = 1ll*a*a%p) if(x&1) now = 1ll*now*a%p;return now;}
void init(){
    n = rd();m = rd();
    rep(i,1,m) {
        e[i].l = rd(),e[i].r = rd(),e[i].v = rd();
        mat[e[i].l][e[i].l]++;
        mat[e[i].l][e[i].r]--;
    }
}
void get_det()
{
    int cnt = 0;
    rep(i,1,n-1){
        rep(j,i,n-1) if(mat[j][i] != 0){
            if(i != j) rep(k,1,n) swap(mat[i][k],mat[j][k]),cnt++;
            break;
        }
        int tmp = Pow(mat[i][i],p-2);
        rep(j,i+1,n){
            int rate = mul(mat[j][i],tmp);
            rep(k,i,n) mat[j][k] = del(mat[j][k],mul(rate,mat[i][k]));
        }
    }
    det = cnt%2==0?1:-1;
    rep(i,1,n-1) det = mul(det,mat[i][i]);
    det = calc(det,0);
}
void solve()
{
    int ans = 0,sigma = 0,cnt = 0;
    rep(i,1,n){
        rep(j,i,n) if(f[i][j] != 0){
            if(i != j)
            rep(k,1,n) swap(f[i][k],f[j][k]),swap(g[i][k],g[j][k]),cnt++;
            break;
        }
        int tmp = Pow(f[i][i],p-2);
        rep(j,1,n) if(j != i){
            int rate = mul(tmp,f[j][i]);
            sigma = calc(sigma,rate);
            rep(k,1,n){
                f[j][k] = del(f[j][k],mul(rate,f[i][k]));
                g[j][k] = del(g[j][k],mul(rate,g[i][k]));
            }
        }
        sigma = calc(sigma,mul(sigma,cnt));
        rep(j,1,n) f[i][j] = mul(f[i][j],tmp),g[i][j] = mul(g[i][j],tmp);
    }
    rep(i,1,m){
        int x = e[i].l,y = e[i].r,v = e[i].v;
        ans = calc(ans,mul( det,mul( del(g[x][x],g[x][y]) , v ) ) );
    }
    printf("%d\n",ans);
}
int main(){
    init();
    rep(i,1,n) rep(j,1,n) f[i][j] = mat[j][i],g[i][j] = i==j;
    get_det();
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值