【题目描述】:
最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现:
·n个结点的环的生成树个数为n。
·n个结点的完全图的生成树个数为n^(n-2)。
这两个发现让小栋欣喜若狂,由此更加坚定了他继续计算生成树个数的想法,他要计算出各种各样图的生成树数目。
一天,小栋和同学聚会,大家围坐在一张大圆桌周围。小栋看了看,马上想到了生成树问题。如果把每个同学看成一个结点,邻座(结点间距离为1)的同学间连一条边,就变成了一个环。可是,小栋对环的计数已经十分娴熟且不再感兴趣。于是,小栋又把图变了一下:不仅把邻座的同学之间连一条边,还把相隔一个座位(结点间距离为2)的同学之间也连一条边,将结点间有边直接相连的这两种情况统称为有边相连,如图1所示。
小栋发现利用通用方法,因计算过于复杂而很难算出来,而且用其他方法也难以找到更简便的公式进行计算。于是,他将图做了简化,从一个地方将圆桌断开,这样所有的同学形成了一条链,连接距离为1和距离为2的点。
这样生成树的总数就减少了很多。小栋不停的思考,一直到聚会结束,终于找到了一种快捷的方法计算出这个图的生成树个数。可是,如果把距离为3的点也连起来,小栋就不知道如何快捷计算了。现在,请你帮助小栋计算这类图的生成树的数目。
输入包含两个整数k, n,由一个空格分隔。k 表示要将所有距离不超 过k(含k)的结点连接起来,n 表示有n 个结点。
【题目解法】:
注意到K<=5,因此我们不妨记录当前5个的点的连通性
如何表示呢?我们可以考虑使用最小表示法
很显然,我们可以将这5个点中同一个连通块的的点标成一样的编号
每个连通块的编号和最左边的的同一连通块的编号一样
然后从左到右每个连通块的第一个结点编号递增
这样我们用一个6进制压缩即可(其中0表示没有结点)
然后我数了数一共有76个合法的状态
因此我们就可以暴力转移,然后矩阵乘法优化了
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=105;
const int M=10;
const int maxmod=65521;
long long n; int m,tot,a[N],trans[N][N],ans[N][N],c[N][N];
inline long long getint()
{
long long x=0; char c=getchar(); bool flag=false;
while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
if (c=='-') flag=true,c=getchar();
while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
if (flag) return -x; else return x;
}
void init()
{
m=getint(); n=getint(); tot=0;
}
namespace MinExpress
{
int bit[M],first[M];
bool judge(int x)
{
for (int i=m; i>=1; i--) bit[i]=x%6,x/=6; bool zero=true;
for (int i=1; i<=m; i++) {if ((bit[i]==0)&&(!zero)) return false; if (bit[i]!=0) zero=false;}
for (int i=0; i<=m; i++) first[i]=0; int maxid=0;
for (int i=m; i>=0; i--) first[bit[i]]=i,maxid=max(maxid,bit[i]);
for (int i=1; i<=maxid; i++) if (first[i]==0) return false;
for (int i=2; i<=maxid; i++) if (first[i-1]>first[i]) return false;
return true;
}
void solve()
{
int S=1; for (int i=1; i<=m; i++) S=S*6;
for (int i=0; i<S; i++) if (judge(i)) a[++tot]=i;
//printf("%d\n",tot);
}
}
namespace Matrix
{
int bit[M],opt[M],id[M],color[M]; bool v[M];
void Zip(int x)
{
for (int i=m; i>=1; i--) bit[i]=x%6,x/=6;
for (int i=1; i<=m; i++) printf("%d",bit[i]);
}
void solve()
{
for (int i=1; i<=tot; i++)
{
int S=(1<<m)-1;
for (int j=0; j<=S; j++)
{
for (int k=m,x=a[i]; k>=1; k--,x/=6) bit[k]=x%6;
for (int k=m,x=j; k>=1; k--,x/=2) opt[k]=x%2; bool flag=true;
for (int k=1; k<=m; k++) if ((bit[k]==0)&&(opt[k]!=0)) {flag=false; break;}
for (int k=0; k<=6; k++) v[k]=false;
for (int k=1; k<=m; k++)
{
if (opt[k]==0) continue;
if (v[bit[k]]) {flag=false; break;}
v[bit[k]]=true; bit[k]=m+1;
}
if (!flag) continue;
for (int k=1; k<=m; k++) if (v[bit[k]]) bit[k]=m+1;
int newS=0,idx=0; bit[m+1]=m+1;
for (int k=0; k<=6; k++) color[k]=0;
for (int k=1; k<=m+1; k++) color[bit[k]]++;
if ((color[bit[1]]==1)&&(bit[1]!=0)) continue;
for (int k=0; k<=6; k++) id[k]=0;
for (int k=2; k<=m+1; k++)
{
if (bit[k]==0) continue;
if (id[bit[k]]==0) id[bit[k]]=++idx;
newS=newS*6+id[bit[k]];
}
int ii=0;
for (int k=1; k<=tot; k++) if (newS==a[k]) {ii=k; break;}
if (ii==0) printf("fuck\n");
trans[i][ii]++; //Zip(a[i]); printf(" "); Zip(a[ii]); printf("\n");
}
}
}
}
void mul1()
{
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
{
c[i][j]=0;
for (int k=1; k<=tot; k++) c[i][j]=(c[i][j]+(long long)ans[i][k]*trans[k][j]%maxmod)%maxmod;
c[i][j]%=maxmod;
}
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
ans[i][j]=c[i][j];
}
void mul2()
{
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
{
c[i][j]=0;
for (int k=1; k<=tot; k++) c[i][j]=(c[i][j]+(long long)trans[i][k]*trans[k][j]%maxmod)%maxmod;
c[i][j]%=maxmod;
}
for (int i=1; i<=tot; i++)
for (int j=1; j<=tot; j++)
trans[i][j]=c[i][j];
}
void Debug()
{
for (int i=1; i<=tot; i++)
{
for (int j=1; j<=tot; j++)
printf("%d",trans[i][j]);
printf("\n");
}
}
void solve()
{
for (int i=1; i<=tot; i++) ans[i][i]=1;
while (n)
{
if (n%2==1) mul1();
mul2(); n/=2;
}
int S=0,id=0;
for (int i=1; i<=m; i++) S=S*6+1;
for (int i=1; i<=tot; i++) if (a[i]==S) {id=i; break;}
if (id==0) printf("fuck\n");
printf("%d\n",ans[1][id]);
}
int main()
{
init();
MinExpress::solve();
Matrix::solve();
solve();
return 0;
}