zoukankan      html  css  js  c++  java
  • 【JZOJ6569】【GDOI2020模拟】夕张的改造 (kaisou)

    题目大意

    给出一棵树,可以删除其中最多(k)条边,再加上若干条边使得其还是一棵树,问这样的操作能得到多少种不同的树的形态(带编号)。

    (nleq 50, 0leq k leq n)

    思路

    一种升级版的生成树计数。
    设树上的边权值为(1),不存在的边权值为(x),则构造矩阵(g)

    • (i≠j),则(g[i][j]=)((i,j))的权值的相反数。
    • (i=j),则(g[i][j]=)(i)相邻边的权值之和。
    • 矩阵树定理:构造上述矩阵后,该矩阵其任意一个余子式的行列式(=sum_{所有生成树}边权之积)
    • 求行列式的方法:用高斯消元把矩阵消成上三角矩阵后,对角线数字乘积即为其行列式的值。

    现在(x)不确定,所以求出来的行列式实际上是一个多项式。这个多项式的(x^k)的系数,就是使用(k)条边权为(x)的边的方案数,即删去原树中(k)条边,再加上(k)条边形成一棵树的方案数。那么问题就变成求这个多项式的系数了。
    直接求系数不好做,我们可以考虑先求这个多项式的点值表达,即把(x)分别代换成(n)个不同的数求行列式,然后使用高斯消元或拉格朗日插值法还原系数表达。
    最后把(x^0)(x^k)的系数加起来就是答案了。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    typedef long long ll;
    const int N=57;
    const ll P=998244353;
    
    int n,K;
    int a[N][N],f[N];
    ll sum,g[N][N][2],b[N][N],y[N],ans[N];
    ll mtp[N],tmp[N],mtp1[N];
    
    ll pow(ll a,ll b){
    	ll ret=1;
    	for(;b;a=a*a%P,b>>=1)if(b&1)ret=ret*a%P;
    	return ret;
    }
    ll inv(ll x){return pow(x%P,P-2);}
    
    ll solve(){
    	for(int i=1;i<n;++i){
    		for(int j=i+1;j<n;++j){
    			ll res=b[j][i]*inv(b[i][i])%P;
    			for(int k=1;k<n;++k)b[j][k]=(b[j][k]-res*b[i][k]%P+P)%P;
    		}
    	}
    	ll ret=1;
    	for(int i=1;i<n;++i)ret=ret*b[i][i]%P;
    	return ret;
    }
    
    void domtp(){
    	memset(tmp,0,sizeof(tmp));
    	for(int i=0;i<n;++i)for(int j=0;j<2;++j)tmp[i+j]=(tmp[i+j]+mtp[i]*mtp1[j]%P)%P;
    	memcpy(mtp,tmp,sizeof(tmp));
    }
    
    int main(){
    	freopen("kaisou.in","r",stdin);
    	//freopen("kaisou.out","w",stdout);
    	scanf("%d%d",&n,&K);
    	for(int i=2;i<=n;++i)scanf("%d",&f[i]),++f[i],a[i][f[i]]=a[f[i]][i]=1;
    	for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)--g[i][j][a[i][j]],++g[i][i][a[i][j]];
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n;++j)for(int k=1;k<=n;++k)b[j][k]=((g[j][k][0]+P)*i+(g[j][k][1]+P)%P)%P;
    		y[i]=solve();
    	}
    	for(int i=1;i<=n;++i){
    		ll pp=1;
    		memset(mtp,0,sizeof(mtp));
    		mtp[0]=1;
    		for(int j=1;j<=n;++j)if(j!=i){
    			mtp1[1]=1,mtp1[0]=P-j;
    			domtp();
    			pp=pp*(i-j+P)%P;
    		}
    		for(int j=0;j<n;++j)ans[j]=(ans[j]+mtp[j]*inv(pp)%P*y[i]%P)%P;
    	}
    	for(int i=0;i<=K;++i)sum=(sum+ans[i])%P;
    	printf("%lld
    ",sum);
    	return 0;
    }
    
    
  • 相关阅读:
    Error (10327): VHDL error at xd.vhd(17): can't determine definition of operator ""+"" -- found 0 pos
    FPGA 起脚nCEO/IO管教设置问题
    使用Cross-validation (CV) 调整Extreme learning Machine (ELM) 最优参数的实现(matlab)
    Tools that help you scrape web data----帮助你收集web数据的工具
    采集网页数据---Using Java
    使用正则表达式自动对文本按照字符排序
    Apriori算法实例----Weka,R, Using Weka in my javacode
    关于FP-Growth 算法一个很好的ppt-学习分享
    ARFF文件格式
    Weka-学习
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/12748500.html
Copyright © 2011-2022 走看看