题意:在一个n维坐标系中,坐标的范围是0到m - 1,如果两个点坐标只有一个维度的坐标不同则可以相互移动,给出p个点,问任意两个点之间路径为d的个数是多少,答案与p取模。
解法:只需要考虑两个点之间不同的维度的个数,递推方程:f[i][j]表示第i步时维度不同个数为j的路径数,f[i][j] = f[i - 1][j - 1] + f[i - 1][j] + f[i - 1][j + 1],由于d的数据范围很大,用矩阵乘法来优化递推关系,当d为1时,用一个矩阵a[i][j]表示从i个维度不同个数点到j个维度不同个数点的路径数,矩阵a的d次幂就是d次移动后的状态。要用到矩阵的快速幂。
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #define LL long long using namespace std; LL n, m, d, p, q; int point[55][55]; LL ans[55][55], x[55][55]; void mul(LL a[55][55], LL b[55][55]) { LL tmp[55][55] = {0}; for(int i = 0; i <= n; i++) for(int j = 0; j <= n; j++) for(int k = 0; k <= n; k++) { tmp[i][j] += a[i][k] * b[k][j] % p; tmp[i][j] %= p; } memcpy(a, tmp, sizeof(tmp)); return ; } int main() { while(~scanf("%lld%lld%lld%lld%lld", &n, &m, &d, &p, &q)) { for(int i = 0; i < q; i++) for(int j = 0; j < n; j++) scanf("%d", &point[i][j]); memset(ans, 0, sizeof(ans)); memset(x, 0, sizeof(x)); for(LL i = 0; i <= n; i++) { if(i) x[i - 1][i] = (n - i + 1) * (m - 1) % p; x[i][i] = i * (m - 2) % p; if(i < n) x[i + 1][i] = (i + 1) % p; } for(int i = 0; i <= n; i++) ans[i][i] = 1; while(d) { if(d & 1) mul(ans, x); d >>= 1; mul(x, x); } for(int i = 0; i < q; i++) { for(int j = 0; j < q; j++) { int cnt = 0; for(int k = 0; k < n; k++) if(point[i][k] != point[j][k]) cnt++; if(j) printf(" "); printf("%lld", ans[cnt][0]); } puts(""); } } return 0; }
耶【什么鬼