列表

详情


NC17515. [NOI2007]生成树计数

描述

最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现:
·n 个结点的环的生成树个数为n。
·n 个结点的完全图的生成树个数为nn-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 的点也连起来,小栋就不知道如何快捷计算了。现在,请你帮助小栋计算这类图的生成树的数目。

输入描述

输入中包含两个整数k, n,由一个空格分隔。k 表示要将所有距离不超过k(含k)的结点连接起来,n 表示有n 个结点。

输出描述

输出一个整数,表示生成树的个数。由于答案可能比较大,所以你只要输出答案除65521 的余数即可

示例1

输入:

3 5

输出:

75

说明:

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++11(clang++ 3.9) 解法, 执行用时: 21ms, 内存消耗: 496K, 提交时间: 2019-03-05 21:13:01

#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;
}

上一题