Description
最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现:
·n个结点的环的生成树个数为n。
·n个结点的完全图的生成树个数为n^(n-2)。这两个发现让小栋欣喜若狂,由此更加坚定了他继续计算生成树个数的
想法,他要计算出各种各样图的生成树数目。一天,小栋和同学聚会,大家围坐在一张大圆桌周围。小栋看了看,
马上想到了生成树问题。如果把每个同学看成一个结点,邻座(结点间距离为1)的同学间连一条边,就变成了一
个环。可是,小栋对环的计数已经十分娴熟且不再感兴趣。于是,小栋又把图变了一下:不仅把邻座的同学之间连
一条边,还把相隔一个座位(结点间距离为2)的同学之间也连一条边,将结点间有边直接相连的这两种情况统称
为有边相连,如图1所示。
小栋以前没有计算过这类图的生成树个数,但是,他想起了老师讲过的计算任意图的生成树个数的一种通用方法:
构造一个n×n的矩阵A={aij},其中
其中di表示结点i的度数。与图1相应的A矩阵如下所示。为了计算图1所对应的生成数的个数,只要去掉矩阵A的最
后一行和最后一列,得到一个(n-1)×(n-1)的矩阵B,计算出矩阵B的行列式的值便可得到图1的生成树的个数所以
生成树的个数为|B|=3528。小栋发现利用通用方法,因计算过于复杂而很难算出来,而且用其他方法也难以找到更
简便的公式进行计算。于是,他将图做了简化,从一个地方将圆桌断开,这样所有的同学形成了一条链,连接距离
为1和距离为2的点。例如八个点的情形如下:
这样生成树的总数就减少了很多。小栋不停的思考,一直到聚会结束,终于找到了一种快捷的方法计算出这个图的
生成树个数。可是,如果把距离为3的点也连起来,小栋就不知道如何快捷计算了。现在,请你帮助小栋计算这类
图的生成树的数目。
Input
包含两个整数k,n,由一个空格分隔。k表示要将所有距离不超过k(含k)的结点连接起来,n表示有n个结点。
Output
输出一个整数,表示生成树的个数。由于答案可能比较大,所以你 只要输出答案除65521 的余数即可。
从左到右处理,记录最后k个点间的连通性可以进行递推,连通情况最多有Bell(5)=52种,因此可以用矩阵乘法优化递推
#include<bits/stdc++.h> typedef unsigned int u32; typedef u32 mat[57][57]; const u32 P=65521; mat c,x,y; int N,k; long long n; inline u32 fix(u32 x){ return x-(x>>16)*P; } void clr(mat a,u32 v){ for(int i=1;i<=N;a[i][i]=v,++i) for(int j=1;j<=N;++j) a[i][j]=0; } void mul(mat a,mat b){ clr(c,0); for(int i=1;i<=N;++i) for(int k=1;k<=N;++k)if(a[i][k]) for(int j=1;j<=N;++j) c[i][j]+=fix(a[i][k]*b[k][j]); for(int i=1;i<=N;++i) for(int j=1;j<=N;++j) a[i][j]=c[i][j]%P; } int ids[33333],idp=0; int tr[57][32],st[57],f[11],g[7][57],ed[11]; void dfs(int w,int m,int v){ if(w==k){ ids[v]=++idp; st[idp]=v; return; } for(int i=0;i<=m;++i)dfs(w+1,m+(i==m),v|i<<3*w); } void upd(int k){ for(int i=0;i<k;++i)ed[i]=-1; for(int i=0,p=0;i<k;++i){ if(ed[f[i]]<0)ed[f[i]]=p++; f[i]=ed[f[i]]; } } void mg(int a,int b,int k){ int z=f[b]; for(int i=0;i<k;++i)if(f[i]==z)f[i]=f[a]; } int main(){ scanf("%d%lld",&k,&n); dfs(0,0,0); N=idp; for(int i=1;i<=idp;++i){ for(int j=0,ab;j<(1<<k);++j){ for(int a=0;a<k;++a)f[a]=st[i]>>a*3&7; ab=1; for(int a=0,pv=-1;a<k;++a)if(j>>a&1){ if(pv<0)pv=a; else if(f[pv]==f[a])ab=0; else mg(pv,a,k); } if(ab){ upd(k); int v=0; for(int a=0;a<k;++a)v|=f[a]<<a*3; tr[i][j]=ids[v]; } for(int a=0;a<k;++a)f[a]=st[i]>>a*3&7; f[k]=k; ab=1; for(int a=0,pv=-1;a<k;++a)if(j>>a&1){ if(f[k]==f[a])ab=0; else mg(k,a,k+1); } if(ab){ ab=0; for(int i=1;i<=k;++i)ab|=f[0]==f[i]; if(ab){ for(int i=0;i<k;++i)f[i]=f[i+1]; upd(k+1); int v=0; for(int a=0;a<k;++a)v|=f[a]<<a*3; ++y[ids[v]][i]; } } } } g[1][idp]=1; for(int t=1;t<k;++t){ int L=1<<t,R=L<<1; for(int i=1;i<=idp;++i)if(g[t][i]){ for(int j=L;j<R;++j){ (g[t+1][tr[i][j]]+=g[t][i])%=P; } } } clr(x,1); for(n-=k;n;n>>=1,mul(y,y))if(n&1)mul(x,y); int ans=0; for(int i=1;i<=idp;++i)ans+=fix(x[1][i]*g[k][i]); printf("%d\n",ans%P); return 0; }