[SP104 HIGH]Highways [HEOI2015]小Z的房间——矩阵树定理入门

矩阵树定理:

用于计算无向连通图的生成树个数。
计算出整张图的度数矩阵D(即 D i , i D_{i,i} Di,i表示i的度数),和邻接矩阵A(即 A i , j A_{i,j} Ai,j表示i和j的连边的数量),然后得到基尔霍夫矩阵(D-A),计算新矩阵的任意n-1阶主子式的绝对值即可。

计算行列式的值:

行列式的值直接计算复杂度太高,于是我们利用类似于高斯消元的方法将行列式消成一个上三角矩阵,不难得出此时除了主对角线之外,行列式值中的其它项为0,于是直接计算主对角线的乘积即可。
由于是行列式的变换,所以每交换一次都要注意答案符号的变化。题目可能要求取模,高斯消元时取模计算不太方便,可以行与行之间辗转相除即可。

题目:

[HEOI2015]小Z的房间
HIGH - Highways
以上两道都是模板题。

// luogu-judger-enable-o2
#include

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj4031.in","r",stdin);
    freopen("bzoj4031.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const ll mod=1e9;
const int maxn=15;
const int maxs=210;
int n,m,num[maxn][maxn],tot;
int dx[3]={0,1,0};
int dy[3]={0,0,1};
ll D[maxs][maxs],A[maxs][maxs],a[maxs][maxs],ans=1;
char s[maxn][maxn];

bool judge(int x,int y){return x>=1 && x<=n && y>=1 && y<=m && s[x][y]!='*';}

void init(){
    read(n); read(m);
    REP(i,1,n)scanf("%s",s[i]+1);
    REP(i,1,n)REP(j,1,m)if(s[i][j]=='.')
        num[i][j]=++tot;
    REP(i,1,n)REP(j,1,m)if(s[i][j]=='.'){
        REP(k,1,2){
            int nx=i+dx[k],ny=j+dy[k];
            if(!judge(nx,ny))continue;
            int num1=num[i][j],num2=num[nx][ny];
            ++D[num1][num1]; ++D[num2][num2];
            A[num1][num2]=A[num2][num1]=1;
        }
    }
    REP(i,1,tot)REP(j,1,tot)a[i][j]=D[i][j]-A[i][j];
}

void print(){
    REP(i,1,tot)cout<<"---";
    cout<<endl;
    REP(i,1,tot){
        REP(j,1,tot)cout<<a[i][j]<<" ";
        cout<<endl;
    }
    REP(i,1,tot)cout<<"---";
    cout<<endl;
}

void work(){
    --tot;
    REP(i,1,tot){
        REP(j,i+1,tot){
            while(a[j][i]){
                ll t=a[i][i]/a[j][i];
                REP(k,i,tot)a[i][k]=(a[i][k]-t*a[j][k])%mod,swap(a[i][k],a[j][k]);
                ans*=-1;
            }
        }
        ans=ans*a[i][i]%mod;
    }
    printf("%lld\n",(ans+mod)%mod);
}

int main(){
//	File();
    init();
    work();
    return 0;
}

#include

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("spoj104.in","r",stdin);
    freopen("spoj104.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int maxn=12+10;
int T,n,m;
ll D[maxn][maxn],A[maxn][maxn],a[maxn][maxn],ans;

int main(){
//	File();
    read(T);
    while(T--){
        ans=1;
        memset(D,0,sizeof(D));
        memset(A,0,sizeof(A));
        memset(a,0,sizeof(a));
        read(n); read(m);
        int u,v;
        REP(i,1,m){
            read(u); read(v);
            ++D[u][u]; ++D[v][v];
            A[u][v]=A[v][u]=1;
        }
        REP(i,1,n)REP(j,1,n)a[i][j]=D[i][j]-A[i][j];
        --n;
        REP(i,1,n)REP(j,i+1,n)while(a[j][i]){
            ll t=a[i][i]/a[j][i];
            REP(k,i,n)a[i][k]-=t*a[j][k],swap(a[i][k],a[j][k]);
        }
        REP(i,1,n)ans*=a[i][i];
        printf("%lld\n",abs(ans));
    }
    return 0;
}

你可能感兴趣的:(高斯消元,矩阵树定理)