一看数据范围就知道是矩阵快速幂优化
首先,设当前点为
i
i
,
接着就会发现,不可能与
i−K
i
−
K
之前的点相连,
因此当前点的连通性只与
i−K
i
−
K
到
i−1
i
−
1
共K个点的连通性有关。
于是我们可以用
d(i,S)
d
(
i
,
S
)
来表示当前点为
i
i
,连通性为的情况时的情况数。
使用最小表示法,能够把连通性实质上不同的情况优化到只剩五十几种。
最小表示法,说白了就是用标号表示点所属的连通块,并且编号按出现的顺序从0开始递增。
例如:如果1,2,3属于同一个连通块,4,6属于同一个连通块,5,7各属于一个连通块,用最小表示法就能表示成0001213。
接着是转移。转移方程可以大致写成是:
d(i,S)=∑d(i−1,Slast)
d
(
i
,
S
)
=
∑
d
(
i
−
1
,
S
l
a
s
t
)
为了转移,我们可以让点
i
i
与前个点任意连边,但不能形成环。
此外还有一点:在只有
i−K
i
−
K
为0号连通块时,
i
i
必须与连边
(否则,0号连通块将会与其他连通块彻底分离,无法形成生成树)
于是乎,在连边的过程中,可能有一些连通块合并了,也可能
i
i
<script type="math/tex" id="MathJax-Element-5234">i</script>没有向任何点连边,自己形成了一个连通块,总之,连边后要重新将状态表示成最小表示法。
由于本DP的每一步转移都十(yi)分(mu)类(yi)似(yang),而且类似线性递推,因此可以用矩阵乘法优化,50*50*50毫无压力。
代码如下:
#include <bits/stdc++.h>
#define LL long long
#define MOD 65521
using namespace std;
const int num[6]={1,1,1,3,16,125};
LL n,m,H[60],s,A[60],ans,d[6][60][6],code[6],cnt[6];
map<LL,int> IH;
struct Matrix
{
LL a[60][60];
Matrix()
{
memset(a,0,sizeof(a));
}
inline Matrix friend operator*(const Matrix &m1,const Matrix &m2)
{
Matrix m3;
for(int i=0;i<s;i++)
{
for(int j=0;j<s;j++)
{
for(int k=0;k<s;k++)
{
(m3.a[i][j]+=(m1.a[i][k]*m2.a[k][j]))%=MOD;
}
}
}
return m3;
}
}M;
void getHash(int k,int x,int y)//k层数,x当前允许最大编号,y当前哈希值
{
if(k==m)
{
IH[y]=s;
H[s++]=y;
return;
}
for(int i=0;i<=x;i++)
{
getHash(k+1,i==x?x+1:x,y|(i<<(3*k)));
}
}
int recode(int &x,int t=0)
{
memset(code,-1,sizeof(code));
for(int i=0,j=0;i<m;i++)
{
int y=x>>(3*i)&7;
if(code[y]==-1)
{
code[y]=j++;
}
x=x^(y<<3*i)^(code[y]<<3*i);
}
return code[t];
}
void dp()
{
for(int i=0;i<m;i++)
{
for(int j=0;j<s;j++)
{
bool flag=(i==0);//急需拯救状态
int t=H[j],x=t>>(3*i)&7;
for(int k=1;k<m;k++)
{
if((t<<(3*k)&7)==0)flag=0;
}
if(flag)
{
(d[i+1][j][0]=d[i][j][m])%=MOD;
continue;
}
for(int k=0;k<=m;k++)
{
(d[i+1][j][k]+=d[i][j][k])%=MOD;//不连线的状态
if(k==x)
{
continue;//不可成环
}
int tt=t,tk=k,tx=x;
if(tx<tk)swap(tx,tk);
for(int l=0;l<m;l++)
{
if(tx==(t>>(3*l)&7))
{
tt=tt^(tx<<3*l)^(tk<<3*l);//连通块合并
}
}
tk=recode(tt,tk);//合并后重编号
(d[i+1][IH[tt]][tk]+=d[i][j][k])%=MOD;//联线状态
}
}
}
}
void getM()
{
for(int i=0;i<s;i++)
{
memset(d,0,sizeof(d));
d[0][i][m]=1;
dp();
for(int j=0;j<s;j++)
{
bool flag=1;
for(int k=1;k<m;k++)
{
if((H[j]>>(3*k)&7)==0)flag=0;
}
for(int k=0;k<=(flag?0:m);k++)
{
int t=H[j]>>3|(k<<(3*m-3));
recode(t);
M.a[i][IH[t]]+=d[m][j][k];
}
}
}
}
Matrix qpow(Matrix x,LL y)
{
Matrix ans;
for(int i=0;i<s;i++)ans.a[i][i]=1;
while(y)
{
if(y&1)ans=ans*x;
x=x*x;
y>>=1;
}
return ans;
}
void getans()
{
for(int i=0;i<s;i++)
{
memset(cnt,0,sizeof(cnt));
A[i]=1;
for(int j=0;j<m;j++)
{
cnt[H[i]>>(3*j)&7]++;
}
for(int j=0;j<m;j++)
{
(A[i]*=num[cnt[j]])%=MOD;
}
(ans+=A[i]*M.a[i][0])%=MOD;
}
}
int main()
{
cin>>m>>n;
getHash(0,0,0);
getM();
M=qpow(M,n-m);
getans();
cout<<ans<<endl;
return 0;
}