看数据,k<=5,n<=1e15,n极大,显然只能矩阵倍增去算。考虑dp,k极小,每个点最多能和前k个点连边,因此我们需要知道前k个点的联通情况,采用状压dp,用最小表示法。
我们可以先用一个DFS预处理出所有可能出现的连通性的状态。然后再枚举连通性状态S以及下一个点和S里的K个点中的哪些点连边,再判断从连通性状态S转移出来的新状态S′是否是合法的,若合法,在邻接矩阵里,标记从S到S′的方案数加1.
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 65521
int const N=60;
int m,temp[7],sta[N][7],tot=0,ini[N],ans=0;
bool f[7],edge[7];//f[i]--i连通块是否已连过,edge[i]-i位置是否连边了
ll n;
struct matrix{
int mat[N][N];
matrix(bool ff){
memset(mat,0,sizeof(mat));
if(ff) for(int i=1;i<=tot;++i) mat[i][i]=1;
}
matrix operator*(matrix b){
matrix re(0);
for(int i=1;i<=tot;++i)
for(int j=1;j<=tot;++j)
for(int k=1;k<=tot;++k)
re.mat[i][j]=(re.mat[i][j]+(ll)mat[i][k]*b.mat[k][j])%mod;
return re;
}
matrix operator^(ll k){
matrix re(1),base(0);
memcpy(base.mat,mat,sizeof(mat));
for(;k;k=k>>1,base=base*base)
if(k&1) re=re*base;
return re;
}
}trans(0);
void dfs1(int i,int x){//前i-1个点分成了x个连通块
if(i==m+1){
++tot;
for(int j=1;j<=m;++j) sta[tot][j]=temp[j];return;
}
for(int j=1;j<=x+1;++j){
temp[i]=j;
dfs1(i+1,max(j,x));
}
}
inline bool issame(int a[],int b[]){
for(int i=1;i<=m;++i)
if(a[i]!=b[i]) return false;
return true;
}
inline int pow(int x,int k){
int re=1;if(k<0) return re;
for(;k;k>>=1,x*=x) if(k&1) re*=x;
return re;
}
void get1(int id){//求sta[id]此时转移到的状态a
int a[7];
memcpy(a,sta[id],sizeof(sta[id]));
for(int i=1;i<=m;++i) if(edge[i]){
if(a[m+1]==0) a[m+1]=a[i];
else{
int x=a[i];
for(int j=1;j<=m;++j) if(a[j]==x) a[j]=a[m+1];
}
}
for(int i=1;i<=m;++i) a[i]=a[i+1];//把状态往前挪一个
int num[7],cnt=0;memset(num,0,sizeof(num));
for(int i=1;i<=m;++i){//重新标号,使得满足最小表示法
if(!num[a[i]]) num[a[i]]=++cnt;
a[i]=num[a[i]];
}
for(int i=1;i<=tot;++i) if(issame(a,sta[i])){
trans.mat[id][i]++;return;
}
}
void dfs2(int id,int i){//搜出所有sta[id]能转移到的状态,枚举下一个点与哪些点连边
if(i==m+1){get1(id);return;}
dfs2(id,i+1);
if(!f[sta[id][i]]){
f[sta[id][i]]=1;edge[i]=1;dfs2(id,i+1);
f[sta[id][i]]=0;edge[i]=0;
}
}
void getinit(int id){
int cnt[7];
memset(cnt,0,sizeof(cnt));ini[id]=1;
for(int i=1;i<=m;++i) cnt[sta[id][i]]++;
for(int i=1;cnt[i]!=0;++i) ini[id]*=pow(cnt[i],cnt[i]-2);return;
//n个点的完全图的生成树个数为n^(n-2)个
}
int main(){
// freopen("a.in","r",stdin);
scanf("%d%lld",&m,&n);
dfs1(1,0);//预处理出k个点的所有连接状态,用最小表示法表示
for(int i=1;i<=tot;++i) getinit(i);//预处理所有状态的初始种类数
for(int i=1;i<=tot;++i){
memset(f,0,sizeof(f));
memset(edge,0,sizeof(edge));
bool flag=1;
for(int j=2;j<=m;++j)
if(sta[i][j]==1){flag=0;break;}
if(flag){//1必须连这个了。
f[1]=1;edge[1]=1;dfs2(i,2);
}
else dfs2(i,1);
}
trans=trans^(n-m);
for(int i=1;i<=tot;++i) ans=(ans+(ll)trans.mat[i][1]*ini[i])%mod;
printf("%d\n",ans);
return 0;
}