HDOJ 2294 - Pendant

Matrix Multiplication (& Quick Power)


Description

有个高富帅,要送个很装逼的吊坠给他女朋友。

他有k种珠子,然后要串成一个 珠子个数小于等于n 的链子。

因为要够装逼,所以这k种珠子都必须要用到。

好了,输入n和k。

输出他可以做出多少种不一样的项链。


Type

Matrix Multiplication

Quick Power


Analysis

一看又是爽歪歪的递推题。

我们来考虑如何递推出。

设f(x, y)为前x个位置,搞了y种不同的珠子。

然后我们考虑f(x, y)和f(x – 1, ...)关系。

而前后的关系,就差在第x个位置,要放什么类型的珠子。

我们可以放前面x – 1个位置已经放过的珠子,有y种颜色可以选。

那么就有 f(x – 1, y) * y种。

也可以放前面x – 1个位置还没放过的珠子,有k- (y – 1)种颜色可以选。

那么就有 f(x – 1, y – 1) * (k – y + 1) 种。

所以我们就得到递推公式 —— f(x, y) = f(x – 1, y) * y + f(x – 1, y – 1) * (k – y + 1)

好了,有了递推公式就碉堡了,剩下的就是矩阵乘法和快速幂。

当然,也要注意一些小的细节。

我们所求的不是f(x, y),而是f(1, y) ~ f(x, y)的总和(因为珠子个数是小于等于n嘛)。

因此我们需要在矩阵的解向量中,增加一项sum(x – 1),且 sum(x – 1) = sum(x – 2) + f(x - 1, y)。

这题对1234567891取余,有点那啥。

在矩阵乘法中,我们通常是每一个矩阵元素计算完后,再去取余,这样可以减少大量运算时间

(如果你不是这样的,那我告诉你,真的省很多)。

而这题由于这个除数真TMD的大,矩阵相乘的时候,两个元素相乘然后加加加加加...

最后你懂的,就超过long long的范围,溢出了,囧。

深一步讲,因为long long = int * int,而除数过于接近int。

导致我们计算的数接近于int * int * order(矩阵的阶),就爆了。


好吧,怎么办?

一种办法是用unsigned long long,的确可以A,速度还蛮快。

当然如果感觉不够妥妥的,可以每次在计算的时候,做完int * int马上mod,就不会悲剧啦。

(如果你现在不知道我在讲什么,那就等到你在写矩阵相乘函数的时候,你就懂了~)


Solution

// HDOJ 2294
// pendant
// by A Code Rabbit

#include <cstdio>
#include <cstring>

const int MAXO = 33;
const int MOD = 1234567891;

template <typename T>
struct Matrix {
    T e[MAXO][MAXO];
    int o;
    Matrix(int ord) { memset(e, 0, sizeof(e)); o = ord; }
    Matrix operator*(const Matrix& one) {
        Matrix res(o);
        for (int i = 0; i < o; i++)
            for (int j = 0; j < o; j++)
                for (int k = 0; k < o; k++)
                    res.e[i][j] += e[i][k] * one.e[k][j];
        return res;
    }
    Matrix operator%(int mod) {
        for (int i = 0; i < o; i++)
            for (int j = 0; j < o; j++)
                e[i][j] %= mod;
        return *this;
    }
};

template <typename T>
T QuickPower(T rdx, int exp, int mod) {
    T res = rdx;
    exp--;
    while (exp) {
        if (exp & 1) res = res * rdx % mod;
        exp >>= 1;
        rdx = rdx * rdx % mod;
    }
    return res;
}

int n, k;

int main() {
    int tot_case;
    scanf("%d", &tot_case);
    while (tot_case--) {
        // Input.
        scanf("%d%d", &n, &k);   
        // Sovle.
        Matrix<unsigned long long> mat_one(k + 1);
        mat_one.e[0][0] = 1;
        mat_one.e[k][0] = 1;
        for (int i = 1; i < k + 1; ++i) {
            mat_one.e[i][i] = i;
            if (i > 1) mat_one.e[i - 1][i] = k - i + 1;
        }
        Matrix<unsigned long long> mat_ans = QuickPower(mat_one, n, MOD);
        // Output.
        printf("%d\n", k * mat_ans.e[1][0] % MOD);
    }

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值