UVA 11651 - Krypton Number System(DP+矩阵快速幂)

UVA 11651 - Krypton Number System

题目链接

题意:给一个进制base,一个分数score求该进制下,有多少数满足一下条件:
1、没有连续数字
2、没有前导零
3、分数为score,分数的计算方式为相邻数字的平方差的和

思路:先从dp入手,dp[i][j]表示组成i,最后一个数字为j的种数,然后进行状态转移,推出前面一步能构成的状态,也就是到dp[(b - 1) * (b - 1)][x]。

然后可以发现后面的状态,都可以由前面这些状态统一转移出来,这样就可以利用矩阵快速幂进行优化,时间复杂度为O(n^3log(score))

这题矩阵快速幂姿势不够优美还会超时,矩阵相乘时候需要优化

代码:

#include <cstdio>
#include <cstring>

typedef unsigned long long ll;
const int N = 155;
const ll MOD = (1ULL<<32);

int n;

struct Mat {
    ll v[N][N];

    Mat() {
	memset(v, 0, sizeof(v));
    }

    void init() {
	memset(v, 0, sizeof(v));
    }

    void init_one() {
	memset(v, 0, sizeof(v));
	for (int i = 0; i < n; i++)
	    v[i][i] = 1;
    }

    Mat operator * (Mat c) {
	Mat ans;
	for (int k = 0; k < n; k++) {
	    for (int i = 0; i < n; i++) {
		if (!v[i][k]) continue;
		for (int j = 0; j < n; j++)
		    ans.v[i][j] = (ans.v[i][j] + v[i][k] * c.v[k][j] % MOD) % MOD;
	    }
	}
	return ans;
    }
} B;

Mat pow_mod(Mat x, int k) {
    Mat ans;
    ans.init_one();
    while (k) {
	if (k&1) ans = ans * x;
	x = x * x;
	k >>= 1;
    }
    return ans;
}

ll dp[40][10], A[N];
int t, b, s, m;

void init() {
    scanf("%d%d", &b, &s);
    m = (b - 1) * (b - 1);
    n = m  * b;
    memset(dp, 0, sizeof(dp));
    for (int i = 0; i < b; i++)
	dp[0][i] = 1;
    for (int i = 1; i <= m; i++) {
	for (int j = 0; j < b; j++) {
	    for (int k = 0; k < b; k++) {
		if (k == j) continue;
		int tmp = (k - j) * (k - j);
		if (i - tmp < 0) continue;
		dp[i][j] = (dp[i][j] + dp[i - tmp][k]) % MOD;
	    }
	}
    }
}

void build() {
    B.init();
    for (int i = b; i < n; i++)
	B.v[i][i - b] = 1;
    for (int i = 1; i <= m; i++) {
	for (int j = 0; j < b; j++) {
	    for (int k = 0; k < b; k++) {
		if (j == k) continue;
		if (i + (k - j) * (k - j) == m + 1) {
		    B.v[(i - 1) * b + j][n - b + k] = 1;
		}
	    }
	}
    }
}

ll solve() {
    ll ans = 0;
    if (s <= m) {
	for (int i = 1; i < b; i++)
	    ans = (ans + dp[s][i]) % MOD;
	return ans;
    }
    for (int i = 1; i <= m; i++) {
	for (int j = 0; j < b; j++)
	    A[(i - 1) * b + j] = dp[i][j];
    }
    build();
    B = pow_mod(B, s - m);
    for (int i = n - b + 1; i < n; i++) {
	for (int j = 0; j < n; j++)
	    ans = (ans + A[j] * B.v[j][i] % MOD) % MOD;
    }
    return ans;
}

int main() {
    int cas = 0;
    scanf("%d", &t);
    while (t--) {
	init();
	printf("Case %d: %llu\n", ++cas, solve());
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值