寻找状态转移方程,
题解参考处
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const ull mod=(1ull<<32);
int N;
ull dp[40][40];
struct matrix{
ull x[155][155];
};
matrix multi(matrix a,matrix b){
matrix tmp;
memset(tmp.x,0,sizeof(tmp.x));
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
for(int k=1;k<=N;k++){
tmp.x[i][j]=(tmp.x[i][j]+a.x[i][k]*b.x[k][j]%mod)%mod;
}
return tmp;
}
matrix quick_multi(matrix a,int n){
matrix tmp;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
tmp.x[i][j]=(i==j);
while(n){
if(n&1)
tmp=multi(tmp,a);
a=multi(a,a);
n>>=1;
}
return tmp;
}
int main(){
int T,b,s,t=0;
scanf("%d",&T);
while(T--){
t++;
scanf("%d%d",&b,&s);
N=b*(b-1)*(b-1);
memset(dp,0,sizeof(dp));
int maxs=(b-1)*(b-1);
for(int i=1;i<b;i++)
dp[0][i]=1;
for(int i=1;i<=maxs;i++){ //状态转移
for(int j=0;j<b;j++)
for(int k=0;k<b;k++){ //在尾数为k的基础上加上一位j,尾数为j
int tmp=(k-j)*(k-j); //多出的分数为(k-j)*(k-j)
if(!tmp||tmp>i) continue;
dp[i][j]+=dp[i-tmp][k]%mod;
}
}
ull sum=0;
if(s<maxs){
for(int i=0;i<b;i++)
sum+=dp[s][i]%mod;
printf("Case %d: %llu\n",t,sum);
continue;
}
matrix st;
memset(st.x,0,sizeof(st.x));
for(int i=1;i<=N-b;i++)
st.x[i][i+b]=1;
for(int i=0;i<b;i++){
for(int j=0;j<b;j++){
if(i!=j){
int mp=(i-j)*(i-j);
st.x[N-b+i+1][(maxs-mp)*b+j+1]=1;
}
}
}
st=quick_multi(st,s-maxs+1);
for(int i=N-b+1;i<=N;i++)
for(int j=1;j<=N;j++)
sum=(sum+dp[(j-1)/b][(j-1)%b]*st.x[i][j]%mod)%mod;
//sum+=(dp[(j-1)/b][(j-1)%b]*st.x[i][j]%mod)%mod; 这样答案就不一样了,T.T ,卡这里很久
printf("Case %d: %llu\n",t,sum);
}
return 0;
}