1 条题解

  • 0
    @ 2025-8-24 22:09:46

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Heartlessly
    AFO

    搬运于2025-08-24 22:09:46,当前版本为作者最后更新于2019-05-05 07:29:16,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    Description

    给定一个长度为 n (1n1018)n\ (1 \leq n \leq 10^{18}) 的序列和 22可重 集合:PRPRNFNF 。现在要把它分成若干块,块的大小有限制,允许的块长为 PRNFPR \cap NF。求有多少种不同的分块方案,答案对 109+710^9 + 7 取模。(设最大块长为 xx1PR,NF,x1001 \leq |PR|,|NF|,x \leq 100)。

    Solution

    60pts60 \rm pts

    考虑 动态规划

    先预处理出所有可能的块长 a1ama_1 \sim a_m(注意要去重)。

    fif_i 表示长度为 ii 的序列的分块方案数。

    初始 f0=1f_0 = 1 。(长度为 00 的序列一定有 11 种分块方案)

    状态转移方程(考虑分出来最后一块的块长为 aja_j):

    EwtYkD.png

    直接转移是 O(n)O(n) 的。

    100pts100\rm pts

    发现块长最大只有 100100maxi=1m{ai}100\max\limits_{i = 1}^m \{a_i\} \leq 100),所以考虑用 矩阵乘法 优化上述 DP

    若矩阵的大小为 size×sizesize \times size,则有 size=maxi=1m{ai}size = \max\limits_{i = 1}^m \{a_i\}(因为 fif_i 不可能由 fi100f_{i - 100} 之前的转移过来)。

    先构造初始矩阵。根据 f0=1f_0 = 1DP 预处理出 f1fsize1f_1 \sim f_{size - 1},然后才能转移。

    EwtNfH.png

    接下来构造转移矩阵。首先根据 fi=j=1mfiajf_i = \sum\limits_{j = 1}^m f_{i - a_j} 得知,矩阵第一行中,第 aia_i 个数为 11,其余为 00 。剩下的 fi1fisize+1f_{i-1} \sim f_{i - size + 1} 都可以在上一个矩阵中找到,所以对应的标为 11,其它为 00

    Ewtapd.png

    最后是答案矩阵。

    Ewttte.png

    答案矩阵的第 11 行第 11 列即是最终答案。时间复杂度为 O(size3logn)O(size^3\log n)

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    template <class T>
    inline void read(T &x) {
        x = 0;
        char c = getchar();
        bool f = 0;
        for (; !isdigit(c); c = getchar()) f ^= c == '-';
        for (; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
        x = f ? -x : x;
    }
    
    template <class T>
    inline void write(T x) {
        if (x < 0) {
            putchar('-');
            x = -x;
        }
        T y = 1;
        int len = 1;
        for (; y <= x / 10; y *= 10) ++len;
        for (; len; --len, x %= y, y /= 10) putchar(x / y + 48);
    }
    
    const int MAXN = 100;
    const int MOD = 1e9 + 7;
    LL n;
    int pr, nf, m, size, f[MAXN + 5];
    bool a[MAXN + 5], b[MAXN + 5];
    
    struct Matrix {
        int mat[MAXN + 5][MAXN + 5];
    
        inline void clear() {
            memset(mat, 0, sizeof (mat));
        }
        inline Matrix friend operator*(Matrix a, Matrix b) {
            Matrix c;
            c.clear();
            for (int i = 1; i <= size; ++i)
                for (int j = 1; j <= size; ++j)
                    for (int k = 1; k <= size; ++k)
                        c.mat[i][j] = (c.mat[i][j] + (LL) a.mat[i][k] * b.mat[k][j]) % MOD;
            return c;
        }//矩阵乘法 
    } ans, base;
    
    inline Matrix quickPow(Matrix x, LL p) {
        Matrix res;
        res.clear();
        for (int i = 1; i <= size; ++i) res.mat[i][i] = 1;
        for (; p; p >>= 1, x = x * x)
            if (p & 1) res = res * x;  
        return res;
    }//矩阵快速幂 
    
    int main() {
        read(n), read(pr);
        for (int x, i = 1; i <= pr; ++i) {
            read(x);
            a[x] = 1;
        }
        read(nf);
        for (int x, i = 1; i <= nf; ++i) {
            read(x);
            b[x] = 1;
        }//用两个桶标记允许的块长 
        for (int i = 1; i <= 100; ++i)
            if (a[i] && b[i]) {//若同时满足 a,b 
                base.mat[1][i] = 1;//转移矩阵第一行 
                size = i;//矩阵大小 
            }
        for (int i = 1; i < size; ++i) base.mat[i + 1][i] = 1;//转移矩阵第 2 ~ size 行 
        f[0] = 1;//初始状态 
        for (int i = 1; i < size; ++i)
            for (int j = 1; j <= i; ++j)
                if (a[j] && b[j])
                    f[i] = (f[i] + f[i - j]) % MOD;//DP 预处理 f[1] ~ f[size - 1] 
        for (int i = 1; i <= size; ++i) ans.mat[i][1] = f[size - i];//初始矩阵 
        ans = quickPow(base, n - size + 1) * ans;//得到答案矩阵 
        write(ans.mat[1][1]);
        putchar('\n');
        return 0;
    }
    
    • 1

    信息

    ID
    4311
    时间
    1000ms
    内存
    125MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者