[JZOJ5153]树形图求和

题目大意

给定一个 n 个点 m 条边的带权有向图,每条边描述为 (ui,vi,wi) 不存在自环,可能有重边。
请计算出所有的以 n 为根的有向生成树(在本题定义为所有边从儿子指向父亲)的权值和,一棵树的权值定义为其所有边的权值和。

2n300,0m105,1ui,vin,1wi109


题目分析

在我的Matrix-Tree定理学习小记(详细介绍+证明)中你可以找到基尔霍夫定理在有向图上的推广。
如果只是求方案数的话直接有向图基尔霍夫矩阵树定理做,求出 Mnn 就好了,可是这题里面我们要计算权值和。
怎么办呢?我们定义一种新的数 (a,b) ,它代表了权值和为 b 的一个局部生成树有 a 种方案。定义其加法为直接相加,表示两种互相独立的方案,减法是加法逆运算;乘法为 (ab,ad+bc) ,意义为两种方案的合并。定义其除法为乘法的逆运算,即 (ac,bcadc2) 。单位元为 (1,0) ,零元为 (0,0)
然后用这种新的数代替原本的数做基尔霍夫定理就好了。加入一条边直接在邻接矩阵对应位置加上 (1,wi) ,度数矩阵类似。消元过程和普通的数的运算一致,要把下三角消成零元。然后答案就是行列式得出来的数的第二项。
时间复杂度 O(n3)


代码实现

#include 
#include 
#include 
#include 
#include 

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int P=1000000007;
const int N=305;

typedef pair<int,int> PI;

#define mkp(a,b) make_pair(a,b)
#define ft first
#define sd second

int sqr(int x){return 1ll*x*x%P;}

int quick_power(int x,int y)
{
    int ret=1;
    for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;
    return ret;
}

PI operator+(PI x,PI y){return mkp((x.ft+y.ft)%P,(x.sd+y.sd)%P);}
PI operator+=(PI &x,PI y){return x=x+y;}
PI operator-(PI x){return mkp(P-x.ft,P-x.sd);}
PI operator-(PI x,PI y){return x+(-y);}
PI operator-=(PI &x,PI y){return x=x-y;}
PI operator*(PI x,PI y){return mkp(1ll*x.ft*y.ft%P,(1ll*x.ft*y.sd%P+1ll*x.sd*y.ft%P)%P);}
PI operator*=(PI &x,PI y){return x=x*y;}
PI operator/(PI x,PI y)
{
    int inv=quick_power(y.ft,P-2);
    return mkp(1ll*x.ft*inv%P,1ll*(1ll*x.sd*y.ft%P-1ll*x.ft*y.sd%P+P)%P*sqr(inv)%P);
}

PI tmp[N][N];

struct matrix
{
    PI num[N][N];
    int s;

    PI det()
    {
        memcpy(tmp,num,sizeof num);bool sig=1;
        for (int i=0;i<s;++i)
        {
            if (!tmp[i][i].ft)
                for (int j=i+1;j<s;++j)
                    if (tmp[j][i].ft)
                    {
                        sig^=1;
                        for (int k=i;k<s;++k) swap(tmp[i][k],tmp[j][k]);
                        break;
                    }
            if (!tmp[i][i].ft) continue;
            PI inv=mkp(1,0)/tmp[i][i],mul;
            for (int j=i+1;j<s;++j)
                if (tmp[j][i].ft)
                {
                    mul=tmp[j][i]*inv;
                    for (int k=i;k<s;++k) tmp[j][k]-=tmp[i][k]*mul;
                }
        }
        PI ret=mkp(1,0);
        for (int i=0;i<s;++i) ret*=tmp[i][i];
        return sig?ret:-ret;
    }
}Kir;

int n,m;

int main()
{
    freopen("calc.in","r",stdin),freopen("calc.out","w",stdout);
    n=read(),m=read(),Kir.s=n;
    for (int i=1,x,y,z;i<=m;++i)
    {
        x=read()-1,y=read()-1,z=read(),swap(x,y);
        Kir.num[y][y]+=mkp(1,z),Kir.num[x][y]-=mkp(1,z);
    }
    --Kir.s,printf("%d\n",Kir.det().sd);
    fclose(stdin),fclose(stdout);
    return 0;
}

你可能感兴趣的:(线性代数,高斯消元,矩阵树定理,生成树,纪中OJ)