dp[[i][j] = sum(dp[i - 1][k]) (k -> j)
狀態方程,因爲N很大而M很小,所以第一時間可以想到矩陣優化
可能之前沒做過類似的題被卡的很厲害。
另外用C++寫大數真心麻煩。。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 45;
const int maxd = 30005;
char n[maxd],n_two[maxd],s[maxd];
int m,mod,cntx;
struct Matx{
int n,m;
int mat[maxn][maxn];
Matx(){
memset(mat,0,sizeof(mat));
}
friend Matx operator * (Matx p,Matx q){
Matx temp;
memset(temp.mat,0,sizeof(temp.mat));
temp.n = p.n; temp.m = q.m;
for(int i = 0; i < p.n; i++){
for(int j = 0; j < q.m; j++){
for(int k = 0; k < q.n; k++){
temp.mat[i][j] += p.mat[i][k] * q.mat[k][j];
temp.mat[i][j] %= mod;
}
}
}
return temp;
}
};
Matx base,ans,start;
void cost(){
int L = strlen(n);
n[L - 1] --;
for(int i = L - 1; i >= 0; i--){
int e = n[i] - '0';
if(e >= 0) break;
else{
n[i - 1] --;
n[i] += 10;
}
}
int t;
for(t = 0; t < L && n[t] == '0'; t++);
if(t == L) n[L++] = '0';
n[L] = '\0';
strcpy(s,n + t);
strcpy(n,s);
}
void init(){
int t = (1 << m);
base.n = base.m = t;
memset(ans.mat,0,sizeof(ans.mat));
memset(start.mat,0,sizeof(start.mat));
for(int i = 0; i < base.n; i++){
for(int j = 0; j < base.m; j++){
base.mat[i][j] = 1;
for(int k = 0; k < m - 1; k++){
int a1 = (1 << k) & i,a2 = (1 << k) & j;
int b1 = (1 << (k + 1)) & i,b2 = (1 << (k + 1)) & j;
if(!a1 && !b1 && !a2 && !b2){
base.mat[i][j] = 0;
break;
}
else if(a1 && b1 && a2 && b2){
base.mat[i][j] = 0;
break;
}
}
}
}
ans.n = 1; ans.m = t;
for(int i = 0; i < ans.m; i++)
ans.mat[0][i] = 1;
start.n = start.m = t;
for(int i = 0; i < start.n; i++)
start.mat[i][i] = 1;
}
bool can_div2(){
int L = strlen(n);
int v = 0;
for(int i = 0; i < L; i++){
v = v * 10 + n[i] - '0';
v %= 2;
}
if(!v) return true;
return false;
}
void solve_div2(){
int L = strlen(n);
int v = 0,cnt = 0;
for(int i = 0; i < L; i++){
v = v * 10 + n[i] - '0';
int e = v / 2;
s[cnt++] = e + '0';
v %= 2;
}
int t;
for(t = 0; t < cnt && s[t] == '0'; t++);
if(cnt == t) s[cnt++] = '0';
s[cnt] = '\0';
strcpy(n,s + t);
}
void solve_to_two(){
if(strcmp(n,"0") == 0) return;
if(can_div2()) n_two[cntx++] = '0';
else n_two[cntx++] = '1';
solve_div2();
solve_to_two();
}
Matx Pow(){
int cc = 0;
for(int i = 0; i < cntx; i++){
if(n_two[i] == '1'){
start = start * base;
cc ++;
}
base = base * base;
}
ans = ans * start;
int ret = 0;
for(int i = 0; i < ans.m; i++){
ret += ans.mat[0][i];
ret %= mod;
}
printf("%d\n",ret);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
cntx = 0;
scanf("%s%d%d",n,&m,&mod);
cost();
init();
solve_to_two();
Pow();
if(T)
puts("");
}
return 0;
}