解题思路
这是一个动态规划和数论结合的问题。给定:
张牌,每张牌上有一个数字
- 需要选择
张牌,将这些数字相乘得到
- 求使得
的方案数
解决方案:
- 先找出
的所有因子
- 使用动态规划,
表示选
张牌时乘积与
的最大公约数为
的方案数
- 最后统计所有满足条件(
)的方案数
关键点:
- 使用动态规划避免重复计算
- 预处理
的所有因子优化计算
- 注意数据范围,使用long类型避免溢出
代码
#include <bits/stdc++.h>
using namespace std;
// 计算最大公约数
int gcd(int x, int y) {
while(y != 0) {
int t = x % y;
x = y;
y = t;
}
return x;
}
int main() {
int n, K, A, B;
scanf("%d%d%d%d", &n, &K, &A, &B);
// 读入牌面数字
long a[n];
for(int i = 0; i < n; i++)
scanf("%ld", &a[i]);
// 预处理A的所有因子
vector<int> factors;
for(int i = 1; i * i <= A; i++) {
if(A % i == 0) {
factors.push_back(i);
if(A/i != i)
factors.push_back(A/i);
}
}
sort(factors.begin(), factors.end());
// dp[i][j]表示选j张牌时乘积与A的最大公约数为i的方案数
long dp[100010][51] = {0};
// 动态规划过程
for(int i = 0; i < n; i++)
for(int j = K; j > 0; j--) {
if(j == 1) {
dp[gcd(a[i], A)][j] += 1;
break;
}
for(auto &x: factors)
dp[gcd(a[i]*x, A)][j] += dp[x][j-1];
}
// 统计满足条件的方案数
long result = 0;
for(int i = B; i <= A; i++)
result += dp[i][K];
printf("%ld\n", result);
return 0;
}
import java.util.*;
public class Main {
// 计算最大公约数
static int gcd(int x, int y) {
while(y != 0) {
int t = x % y;
x = y;
y = t;
}
return x;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int K = sc.nextInt();
int A = sc.nextInt();
int B = sc.nextInt();
// 读入牌面数字
long[] a = new long[n];
for(int i = 0; i < n; i++)
a[i] = sc.nextLong();
// 预处理A的所有因子
List<Integer> factors = new ArrayList<>();
for(int i = 1; i * i <= A; i++) {
if(A % i == 0) {
factors.add(i);
if(A/i != i)
factors.add(A/i);
}
}
Collections.sort(factors);
// dp[i][j]表示选j张牌时乘积与A的最大公约数为i的方案数
long[][] dp = new long[100010][51];
// 动态规划过程
for(int i = 0; i < n; i++)
for(int j = K; j > 0; j--) {
if(j == 1) {
dp[gcd((int)a[i], A)][j] += 1;
break;
}
for(int x: factors)
dp[gcd((int)(a[i]*x), A)][j] += dp[x][j-1];
}
// 统计满足条件的方案数
long result = 0;
for(int i = B; i <= A; i++)
result += dp[i][K];
System.out.println(result);
}
}
def gcd(x, y):
while y:
x, y = y, x % y
return x
def solve():
n, K, A, B = map(int, input().split())
a = list(map(int, input().split()))
# 预处理A的所有因子
factors = []
i = 1
while i * i <= A:
if A % i == 0:
factors.append(i)
if A//i != i:
factors.append(A//i)
i += 1
factors.sort()
# dp[i][j]表示选j张牌时乘积与A的最大公约数为i的方案数
dp = [[0] * (n+1) for _ in range(100010)]
# 动态规划过程
for i in range(n):
for j in range(K, 0, -1):
if j == 1:
dp[gcd(a[i], A)][j] += 1
break
for x in factors:
dp[gcd(a[i]*x, A)][j] += dp[x][j-1]
# 统计满足条件的方案数
result = sum(dp[i][K] for i in range(B, A+1))
print(result)
solve()
算法及复杂度
- 算法:动态规划 + 数论(GCD)
- 时间复杂度:
- 其中
是A的因子个数的近似值
- 空间复杂度:
- 需要存储动态规划数组