夕张的改造 题解

题面:
提督们惊奇地发现,2019 年实装的改造非常少。
经调查,原来是改造厂的厂长于八日克扣了其他舰娘改造的图纸,并且在 2020 年的第一个月利用这些图纸进行了华丽的改造,一共有三种形态,于八日改二,于八日改二特,于八日改二丁,对空、对陆、对潜、开幕雷、五装备格,无所不能。镇守府雪菜八万钢惨遭退役。
舰娘的结构可以看成一棵\(n\)个点的树,点编号为 \(0 \sim n−1\)。使用一张图纸可以把树中的某一条边去掉,再加上一条边,使得它依然是一棵树。
现在,于八日想在 2020 年继续拿走别的舰娘的图纸对自己进行改造,她一共拿走了\(k\)张图纸。她想知道,自己经过接下来的改造之后,总共会有多少种形态。两个形态不同,表示有一条边 \((x, y)\) 在第一棵树中出现,在另一棵树中不出现。


题解:题目的意思是让我们求一棵树,其中有至多\(k\)条边不在原树中的树的数量。

我们建一张完全图,所以可以给在原树中的每一条边赋值为\(1\),给不在原树中的边赋值为权值\(x\),所以我们所求的就是所有边边权之积\(\leq x^k\)的生成树种类数。

然后我们知道如果令每一棵生成树的价值为所有边边权之积的话,那么可以用矩阵树定理求出所有生成树价值之和,那么在这一题中,我们希望求出的价值之和是一个包含\(x\)\(n-1\)次多项式,其中第\(i\)项系数表示边权之积为\(x^i\)的生成树种类数,最后的答案就是\({x^0} \sim {x^k}\)的系数之和。

我们发现,直接求这个东西不好求,所以用矩阵树定理,求出当\(x\)等于\(1 \sim n\)时该多项式的值,然后用拉格朗日插值就可以推出原多项式。

代码:

#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxn=50;
const int Mod=998244353;
int ksm(int a,int b){
    int ans=1;
    while(b){
        if(b&1){
            ans=1ll*ans*a%Mod;
        }
        a=1ll*a*a%Mod;
        b>>=1;
    }
    return ans;
}
bool mp[Maxn+5][Maxn+5];
int a[Maxn+5][Maxn+5];
int n,m;
int fa[Maxn+5];
int b[Maxn+5],c[Maxn+5],d[Maxn+5];
int work(int n){
    int ans=1;
    for(int i=1;i<n;i++){
        int x=0;
        for(int j=i;j<=n;j++){
            if(a[j][i]!=0){
                x=j;
                break;
            }
        }
        if(x==0){
            continue;
        }
        if(x!=i){
            ans=Mod-ans;
            swap(a[x],a[i]);
        }
        int inv=ksm(a[i][i],Mod-2);
        for(int j=i+1;j<=n;j++){
            int v=1ll*a[j][i]*inv%Mod;
            for(int k=i;k<=n;k++){
                a[j][k]=(a[j][k]-1ll*v*a[i][k]%Mod+Mod)%Mod;
            }
        }
    }
    for(int i=1;i<=n;i++){
        ans=1ll*ans*a[i][i]%Mod;
    }
    return ans;
}
int calc(int x){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            a[i][j]=0;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int val=x;
            if(mp[i][j]){
                val=1;
            }
            a[i][j]=(a[i][j]-val+Mod)%Mod;
            a[j][i]=(a[j][i]-val+Mod)%Mod;
            a[i][i]=(a[i][i]+val)%Mod;
            a[j][j]=(a[j][j]+val)%Mod;
        }
    }
    return work(n-1);
}
int main(){
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=2;i<=n;i++){
        scanf("%d",&u);
        u++;
        v=i;
        mp[u][v]=mp[v][u]=1;
    }
    for(int i=1;i<=n;i++){
        int y=calc(i);
        for(int j=1;j<=n;j++){
            c[j]=0;
        }
        c[0]=1;
        for(int j=1;j<=n;j++){
            if(j!=i){
                if(i<j){
                    y=1ll*y*(Mod-ksm(j-i,Mod-2))%Mod;
                }
                else{
                    y=1ll*y*ksm(i-j,Mod-2)%Mod;
                }
                for(int k=0;k<=n;k++){
                    d[k]=1ll*(Mod-j)*c[k]%Mod;
                    if(k){
                        d[k]=(d[k]+c[k-1])%Mod;
                    }
                }
                for(int k=0;k<=n;k++){
                    c[k]=d[k];
                }
            }
        }
        for(int j=0;j<=n;j++){
            b[j]=(b[j]+1ll*c[j]*y%Mod)%Mod;
        }
    }
    int ans=0;
    for(int i=0;i<=m;i++){
        ans=(ans+b[i])%Mod;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/withhope/p/12382309.html