【XSY1537】五颜六色的幻想乡(矩阵树定理+高斯消元+拉格朗日插值)

版权声明:转载请声明 https://blog.csdn.net/ezoiHQM/article/details/82716757

题意:有 n 个点, m 条有颜色的边,颜色为红色蓝色和绿色,对于所有满足 r + b + g = n 1 的三元组 ( r , b , g ) ,求恰有 r 条红色的边, b 条蓝色的边, g 条绿色的边的生成树个数。

题解:
生成树计数基本上就是矩阵树定理啦。
我们可以给每一种颜色赋一个额外的值(在矩阵树定理的时候邻接矩阵和度数矩阵就加这个值),在这里我们给红边设为 x ,蓝边设为 x n ,绿边设为 1 ,我们会发现当我们再去给这些边去搞矩阵树定理的时候就会发现得到的答案是一个关于 x n 2 次多项式,然后我们会发现 x b n + r ( 0 r < n ) 表示的就是恰有 r 条红色的边, b 条蓝色的边, g 条绿色的边的生成树个数。所以我们就可以进行 n 2 次矩阵树定理+高斯消元,每次给 x 附一个不同的值(我是用 1 n 2 ),然后再拉格朗日插值求出这个多项式的每一个项的系数,于是就解决了这个问题啦。
如果有误在评论区吼一声哦!
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
int n,m,u[2510],v[2510],clr[2510],a[60][60],f[2510],F[2][2510],ans[2510];
int qpow(int x,int n){
    int ret=1;
    while(n){
        if(n&1)
            ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;
        n>>=1;
    }
    return ret;
}
void Plus(int &a,int b){
    a+=b;
    if(a>=mod)
        a-=mod;
    return;
}
void Minus(int &a,int b){
    a-=b;
    if(a<0)
        a+=mod;
    return;
}
int gauss(int n){
    int ret=1;
    for(int i=1;i<n;i++){
        for(int j=i+1;j<=n;j++){
            if(!a[i][i])
                return 0;
            if(!a[j][i])
                continue;
            int tmp=1ll*a[j][i]*qpow(a[i][i],mod-2)%mod;
            for(int k=i;k<=n;k++)
                Minus(a[j][k],1ll*a[i][k]*tmp%mod);
        }
    }
    for(int i=1;i<=n;i++)
        ret=1ll*ret*a[i][i]%mod;
    return ret;
}
void lagrange(int n){
    F[0][0]=1;
    for(int i=1;i<=n;i++){
        memset(F[i&1],0,sizeof(F[i&1]));
        for(int j=0;j<i;j++){
            Plus(F[i&1][j+1],F[i&1^1][j]);
            Plus(F[i&1][j],1ll*F[i&1^1][j]*(mod-i)%mod);
        }
    }
    for(int i=1;i<=n;i++){
        int tmp=1;
        for(int j=1;j<=n;j++)
            if(i!=j)
                tmp=1ll*tmp*(i-j+mod)%mod;
        tmp=1ll*qpow(tmp,mod-2)*f[i]%mod;
        memcpy(F[n&1^1],F[n&1],sizeof(F[n&1]));
        for(int j=n;j;j--){
            Plus(ans[j-1],1ll*F[n&1^1][j]*tmp%mod);
            if(j>0)
                Plus(F[n&1^1][j-1],1ll*F[n&1^1][j]*i%mod);
        }
    }
    return;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",u+i,v+i,clr+i);
    for(int i=1;i<=n*n;i++){
        memset(a,0,sizeof(a));
        for(int k=1;k<=m;k++){
            if(clr[k]==1)
                Minus(a[u[k]][v[k]],i),Minus(a[v[k]][u[k]],i),Plus(a[u[k]][u[k]],i),Plus(a[v[k]][v[k]],i);
            else if(clr[k]==2){
                int tmp=qpow(i,n);
                Minus(a[u[k]][v[k]],tmp),Minus(a[v[k]][u[k]],tmp),Plus(a[u[k]][u[k]],tmp),Plus(a[v[k]][v[k]],tmp);
            }
            else Minus(a[u[k]][v[k]],1),Minus(a[v[k]][u[k]],1),Plus(a[u[k]][u[k]],1),Plus(a[v[k]][v[k]],1);
        }
        f[i]=gauss(n-1);
    }
    lagrange(n*n);
    for(int i=0;i<n;i++)
        for(int j=0;i+j<n;j++)
            printf("%d\n",ans[i+j*n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ezoiHQM/article/details/82716757