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; }
时间: 2024-10-03 00:38:46