题目链接

该问题似乎是特定竞赛的题目,暂无公开链接

题目描述

给定一个初始值 和一个由 个正整数组成的数组 。定义一次操作如下:

  1. 选择数组 中的若干个元素(至少一个),记元素之和为
  2. 更新为

求解有多少种不同的操作方案,使得恰好经过 次操作后 。由于答案可能很大,请将答案对 取模后输出。

记第 次选取元素的下标构成的集合为 ,两个方案被视为不同的,当且仅当存在 使得两个方案第 次选取元素的下标构成的集合不同。

解题思路

这是一个多阶段决策的计数问题。由于操作次数 非常大,而状态空间(模 的余数)相对较小,这强烈地暗示了需要使用矩阵快速幂来进行优化。

动态规划模型

首先,我们可以建立一个基础的动态规划模型。 令 表示经过 次操作后,当前值为 的方案数。我们的目标是求

  • 初始状态: 0 次操作后,只有初始状态 是可达的。因此,,对于所有其他的 ,有

  • 状态转移: 考虑从第 步转移到第 步。假设第 步后值为 ,在第 步操作中,我们选择了一个和为 的非空子集,使得新状态的值为

    为了进行转移,我们首先需要预计算出单次操作所有可能的乘数 (模 意义下)及其对应的方案数。令 为从数组 中选取一个非空子集,使其元素和模 的方案数。

    那么, 可以由所有 的状态转移过来。一个状态 能转移到 ,当且仅当存在一个乘数 使得

矩阵快速幂优化

上述 DP 转移过程可以用矩阵乘法来描述。设 是一个 的列向量,其中 。我们可以构建一个 的转移矩阵 ,使得

根据状态转移方程,矩阵 的第 行第 列的元素 表示从状态 转移到状态 的总方案数。这个方案数等于所有能使 成立的 对应的 之和。

在得到转移关系 后,我们可以推导出 。我们可以使用矩阵快速幂 的时间内计算出

算法步骤

  1. 计算 count 数组: 使用 的动态规划(类似于 0-1 背包)计算出从数组 中任取子集(可为空)得到各个余数 的方案数 ways[v]。然后,count[v] = ways[v],但 count[0] 需要减 1 以排除空集的情况。

  2. 构建转移矩阵 T: 创建一个 的零矩阵。遍历所有旧状态 和所有可能的乘数余数 ,计算出新状态 ,然后将 的值累加到 上。这一步的复杂度是

  3. 矩阵快速幂: 使用矩阵乘法和快速幂算法计算出最终的转移矩阵

  4. 求解: 最终的答案是 。由于 是一个单位向量(只有一个位置是 1,其余是 0),,所以 。答案就是最终矩阵 的第 行,第 列的元素。

代码

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;

const int MOD = 1e9 + 7;

using Matrix = vector<vector<long long>>;

Matrix multiply(const Matrix& a, const Matrix& b, int m) {
    Matrix c(m, vector<long long>(m, 0));
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < m; ++j) {
            for (int l = 0; l < m; ++l) {
                c[i][j] = (c[i][j] + a[i][l] * b[l][j]) % MOD;
            }
        }
    }
    return c;
}

Matrix matrix_pow(Matrix base, int exp, int m) {
    Matrix res(m, vector<long long>(m, 0));
    for (int i = 0; i < m; ++i) res[i][i] = 1;
    while (exp > 0) {
        if (exp % 2 == 1) res = multiply(res, base, m);
        base = multiply(base, base, m);
        exp /= 2;
    }
    return res;
}

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    long long c_init;
    int m, k, t;
    for (int i = 0; i < n; ++i) cin >> a[i];
    cin >> c_init >> m >> k >> t;

    vector<long long> ways(m, 0);
    ways[0] = 1;
    for (int val : a) {
        vector<long long> next_ways = ways;
        int rem = val % m;
        for (int i = 0; i < m; ++i) {
            next_ways[i] = (next_ways[i] + ways[(i - rem + m) % m]) % MOD;
        }
        ways = next_ways;
    }
    
    vector<long long> count = ways;
    count[0] = (count[0] - 1 + MOD) % MOD;

    Matrix T(m, vector<long long>(m, 0));
    for (int l = 0; l < m; ++l) {
        for (int v = 0; v < m; ++v) {
            if (count[v] > 0) {
                int j = (1LL * l * v) % m;
                T[j][l] = (T[j][l] + count[v]) % MOD;
            }
        }
    }
    
    Matrix T_final = matrix_pow(T, t, m);
    
    int c_start_mod = c_init % m;
    
    cout << T_final[k][c_start_mod] << endl;

    return 0;
}
import java.util.Scanner;

public class Main {
    private static final int MOD = 1_000_000_007;

    private static long[][] multiply(long[][] a, long[][] b, int m) {
        long[][] c = new long[m][m];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < m; j++) {
                for (int l = 0; l < m; l++) {
                    c[i][j] = (c[i][j] + a[i][l] * b[l][j]) % MOD;
                }
            }
        }
        return c;
    }

    private static long[][] matrixPow(long[][] base, int exp, int m) {
        long[][] res = new long[m][m];
        for (int i = 0; i < m; i++) {
            res[i][i] = 1;
        }
        while (exp > 0) {
            if (exp % 2 == 1) {
                res = multiply(res, base, m);
            }
            base = multiply(base, base, m);
            exp /= 2;
        }
        return res;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextInt();
        }
        long c_init = sc.nextLong();
        int m = sc.nextInt();
        int k = sc.nextInt();
        int t = sc.nextInt();

        long[] ways = new long[m];
        ways[0] = 1;
        for (int val : a) {
            long[] next_ways = new long[m];
            System.arraycopy(ways, 0, next_ways, 0, m);
            int rem = val % m;
            for (int i = 0; i < m; i++) {
                next_ways[i] = (next_ways[i] + ways[(i - rem + m) % m]) % MOD;
            }
            ways = next_ways;
        }
        
        long[] count = ways;
        count[0] = (count[0] - 1 + MOD) % MOD;
        
        long[][] T = new long[m][m];
        for (int l = 0; l < m; l++) {
            for (int v = 0; v < m; v++) {
                if (count[v] > 0) {
                    int j = (int) ((1L * l * v) % m);
                    T[j][l] = (T[j][l] + count[v]) % MOD;
                }
            }
        }
        
        long[][] T_final = matrixPow(T, t, m);
        
        int c_start_mod = (int) (c_init % m);
        
        System.out.println(T_final[k][c_start_mod]);
    }
}
MOD = 10**9 + 7

def multiply(a, b, m):
    c = [[0] * m for _ in range(m)]
    for i in range(m):
        for j in range(m):
            for l_ in range(m):
                c[i][j] = (c[i][j] + a[i][l_] * b[l_][j]) % MOD
    return c

def matrix_pow(base, exp, m):
    res = [[0] * m for _ in range(m)]
    for i in range(m):
        res[i][i] = 1
    
    while exp > 0:
        if exp % 2 == 1:
            res = multiply(res, base, m)
        base = multiply(base, base, m)
        exp //= 2
    return res

def solve():
    n = int(input())
    a = list(map(int, input().split()))
    c_init, m, k, t = map(int, input().split())

    ways = [0] * m
    ways[0] = 1
    for val in a:
        next_ways = list(ways)
        rem = val % m
        for i in range(m):
            next_ways[i] = (next_ways[i] + ways[(i - rem + m) % m]) % MOD
        ways = next_ways
    
    count = ways
    count[0] = (count[0] - 1 + MOD) % MOD
    
    T = [[0] * m for _ in range(m)]
    for l in range(m):
        for v in range(m):
            if count[v] > 0:
                j = (l * v) % m
                T[j][l] = (T[j][l] + count[v]) % MOD
    
    T_final = matrix_pow(T, t, m)
    
    c_start_mod = c_init % m
    
    print(T_final[k][c_start_mod])

solve()

算法及复杂度

  • 算法:动态规划 (子集和) + 矩阵快速幂
  • 时间复杂度: - 来自于预计算子集和方案数, 来自于矩阵快速幂。
  • 空间复杂度: - 主要用于存储转移矩阵。