题目链接
该问题似乎是特定竞赛的题目,暂无公开链接
题目描述
给定一个初始值 和一个由
个正整数组成的数组
。定义一次操作如下:
- 选择数组
中的若干个元素(至少一个),记元素之和为
。
- 将
更新为
。
求解有多少种不同的操作方案,使得恰好经过 次操作后
。由于答案可能很大,请将答案对
取模后输出。
记第 次选取元素的下标构成的集合为
,两个方案被视为不同的,当且仅当存在
使得两个方案第
次选取元素的下标构成的集合不同。
解题思路
这是一个多阶段决策的计数问题。由于操作次数 非常大,而状态空间(模
的余数)相对较小,这强烈地暗示了需要使用矩阵快速幂来进行优化。
动态规划模型
首先,我们可以建立一个基础的动态规划模型。
令 表示经过
次操作后,当前值为
的方案数。我们的目标是求
。
-
初始状态: 0 次操作后,只有初始状态
是可达的。因此,
,对于所有其他的
,有
。
-
状态转移: 考虑从第
步转移到第
步。假设第
步后值为
,在第
步操作中,我们选择了一个和为
的非空子集,使得新状态的值为
。
为了进行转移,我们首先需要预计算出单次操作所有可能的乘数
(模
意义下)及其对应的方案数。令
为从数组
中选取一个非空子集,使其元素和模
为
的方案数。
那么,
可以由所有
的状态转移过来。一个状态
能转移到
,当且仅当存在一个乘数
使得
。
矩阵快速幂优化
上述 DP 转移过程可以用矩阵乘法来描述。设 是一个
的列向量,其中
。我们可以构建一个
的转移矩阵
,使得
。
根据状态转移方程,矩阵 的第
行第
列的元素
表示从状态
转移到状态
的总方案数。这个方案数等于所有能使
成立的
对应的
之和。
在得到转移关系 后,我们可以推导出
。我们可以使用矩阵快速幂在
的时间内计算出
。
算法步骤
-
计算
count
数组: 使用的动态规划(类似于 0-1 背包)计算出从数组
中任取子集(可为空)得到各个余数
的方案数
ways[v]
。然后,count[v] = ways[v]
,但count[0]
需要减 1 以排除空集的情况。 -
构建转移矩阵
T
: 创建一个的零矩阵。遍历所有旧状态
和所有可能的乘数余数
,计算出新状态
,然后将
的值累加到
上。这一步的复杂度是
。
-
矩阵快速幂: 使用矩阵乘法和快速幂算法计算出最终的转移矩阵
。
-
求解: 最终的答案是
。由于
是一个单位向量(只有一个位置是 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()
算法及复杂度
- 算法:动态规划 (子集和) + 矩阵快速幂
- 时间复杂度:
-
来自于预计算子集和方案数,
来自于矩阵快速幂。
- 空间复杂度:
- 主要用于存储转移矩阵。