UVA11651Krypton Number System(DP+矩阵快速幂)

来源:互联网 发布:实惠猪软件多少钱 编辑:程序博客网 时间:2024/06/06 19:56

由于CSDN编辑的不方便,所以给出一篇别人的讲解:http://www.cnblogs.com/AOQNRMGYXLMV/p/5256508.html

但是他的转移出现了错误。正确的矩阵相乘应该是这样



题目意思是给定一个进制base和一个score,问在该进制下有多少数满足下列条件:相邻两个数字不相同,前缀不能为0,所有相邻数字的差的平方和位score,所有数字都是整数。

这个题目难度主要集中在找递推式子,一开始确实没有思路,看了一个人的博客,知道用DP和矩阵快速幂做,试着去推DP的方程

有dp[i][j]表示当score = i时数字末尾为j的数字的个数。

我们拿4进制简单推一下,首先初始化显然有,然后继续推score = 1的情况:,继续推score = 2的情况:,继续推score = 3的情况:,继续推score =4的情况:

,我说,这样一直递推到8就可以了,为什么呢?因为如果我们把0作为第一个节点,那么0下一次最大只能到达,也就是9,这样0-8就形成了一个循环节。显然0 -> 9, 1 -> 10 ... 8 -> 17都是一样的递推式子。这样我们就得到了递推的关系式,然后就可以用矩阵快速幂优化计算,最后就能得到答案。

 到只需要将第一个矩阵的后4 * 8项依次往前平移4项,然后第二个矩阵最后四项的则需要利用上面的递推关系式得到。我们只需要得到后,利用矩阵快速幂就可以求得任意一项的值

我的心得体会:在初始化 这个dp数组时候有两种初始化方法,也意味着两种不同的理解。第一种是上面的这样,还有一种就是dp[0][..]全部初始化为1,然后计算答案的时候就需要从当score为n时最后一位为1到base-1开始相加。可以这样理解,这样初始化意味着允许出现前导零,但数的最后一位不能是零,这和题目要求其实是一样的作用。

还有就是循环节,因为每次增加一个数score最大只能增加,所以每轮计算(n-1)*(n-1)个score的模式是一样的。代码对于下标的各种用法,还是很有技巧性的。最后还有一点在代码的注释中。。


第一种初始化dp的代码:

#include<iostream>#include<cstring>#include<cstdio>using namespace std;typedef unsigned int LL;const int maxn = 155;int n, m, sz;struct Matrix{    LL mat[maxn][maxn];    Matrix ()    {        memset(mat, 0, sizeof(mat));    }    Matrix operator * (const Matrix& other) const    {        Matrix tem;        for(int i = 0; i < sz; i++)            for(int j = 0; j < sz; j++)                if(mat[i][j])                    for(int k = 0; k < sz; k++)                        tem.mat[i][k] += mat[i][j] * other.mat[j][k];        return tem;    }};Matrix fast_mod(Matrix base, int power){    Matrix ans;    for(int i = 0; i < sz; i++)        ans.mat[i][i] = 1;    while(power)    {        if(power&1)            ans = ans * base;        base = base * base;        power >>= 1;    }    return ans;}LL a[maxn], dp[30][10];int main(){    int T;    scanf("%d", &T);    for(int kase = 1; kase <= T; kase++)    {        scanf("%d%d", &n, &m);        printf("Case %d: ", kase);        int N = (n-1) * (n-1) * n;        memset(dp, 0, sizeof(dp));        for(int i = 1; i < n; i++)            dp[0][i] = 1;        for(int i = 0; i < (n-1)*(n-1); i++)        {            for(int j = 0; j < n; j++)            {                for(int k = 0; k < n; k++)                {                    int d = (k - j) * (k - j);                    if(!d || i + d >= (n-1)*(n-1))                        continue;                    dp[i+d][k] += dp[i][j];                }            }        }        if(m < (n-1)*(n-1))        {            LL ans = 0;            for(int i = 0; i < n; i++)                ans += dp[m][i];            printf("%u\n", ans);            continue;        }        sz = N;        int s = (n - 1) * (n - 1);        for(int i = 0; i < (n - 1) * ( n - 1); i++)            for(int j = 0; j < n; j++)                a[i*n+j] = dp[i][j];        Matrix base;        for(int i = n; i < N; i++)            base.mat[i-n][i] = 1;        for(int i = 0; i < n; i++)            for(int j = 0; j < n; j++)            {                int d = (j - i) * (j - i);                base.mat[N - n + i][n * (s - d) + j] = 1;       //之所以是n * (s - d) + j,因为s-d意味着是从哪个数转移过来的,所以乘n就很好理解了,+j纯属下标的理解            }        base = fast_mod(base, m - (n-1)*(n-1) + 1);        LL ans = 0;        for(int i = N - n; i < N; i++)            for(int j = 0; j < N; j++)                ans += base.mat[i][j] * a[j];        printf("%u\n", ans);    }    return 0;}

第二种初始化代码:(结构体内各种重载!!)

#include <stdio.h>#include <string.h>const unsigned long long mod = 1LLU<<32;struct Matrix {unsigned long long v[150][150];int row, col; // row x colMatrix(int n, int m, long long a = 0) {memset(v, 0, sizeof(v));row = n, col = m;for(int i = 0; i < row && i < col; i++)v[i][i] = a;}Matrix operator*(const Matrix& x) const {Matrix ret(row, x.col);for(int i = 0; i < row; i++) {for(int k = 0; k < col; k++) {if (v[i][k])for(int j = 0; j < x.col; j++) {ret.v[i][j] += v[i][k] * x.v[k][j],ret.v[i][j] %= mod;}}}return ret;}Matrix operator^(const int& n) const {Matrix ret(row, col, 1), x = *this;int y = n;while(y) {if(y&1)ret = ret * x;y = y>>1, x = x * x;}return ret;}};int main() {int testcase, cases = 0, base, score;scanf("%d", &testcase);while(testcase--) {scanf("%d %d", &base, &score);printf("Case %d: ", ++cases);int N = (base - 1) * (base - 1);unsigned long long dp[64][64] = {};for (int i = 0; i <= N; i++)dp[0][i] = 1;for (int i = 0; i < N; i++) {for (int j = 0; j < base; j++) {for (int k = 0; k < base; k++) {int d = (j - k) * (j - k);if (i + d > N || j == k)continue;dp[i+d][k] += dp[i][j];dp[i+d][k] %= mod;}}}if (score <= N) {unsigned long long ret = 0;for (int i = 1; i < base; i++)ret = (ret + dp[score][i])%mod;printf("%llu\n", ret);continue;}int r, c;r = c = (base - 1) * (base - 1) * base;Matrix x(r, c), y(c, 1);for (int i = 1; i <= N; i++)for (int j = 0; j < base; j++)y.v[(i-1) * base+j][0] = dp[i][j];for (int i = base; i < r; i++)x.v[i - base][i] = 1;for (int i = 0; i < base; i++) {for (int j = 0; j < base; j++) {if (i == j)continue;int d = N - (i - j) * (i - j);x.v[(N-1)*base + i][d*base + j] = 1;}}Matrix z = (x ^ (score - N)) * y;unsigned long long ret = 0;for (int i = 1; i < base; i++)ret = (ret + z.v[(N-1) * base + i][0])%mod;printf("%llu\n", ret);}return 0;}



1 0
原创粉丝点击