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

由于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 col
	Matrix(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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值