牛客题解 | 公约数
题目
解题思路
这是一个动态规划和数论结合的问题。给定:
- \(n\) 张牌,每张牌上有一个数字 \(a_i\)
- 需要选择 \(K\) 张牌,将这些数字相乘得到 \(X\)
- 求使得 \(\gcd(X,A) \geq B\) 的方案数
解决方案:
- 先找出 \(A\) 的所有因子
- 使用动态规划,\(dp[i][j]\) 表示选 \(j\) 张牌时乘积与 \(A\) 的最大公约数为 \(i\) 的方案数
- 最后统计所有满足条件(\(\geq B\))的方案数
关键点:
- 使用动态规划避免重复计算
- 预处理 \(A\) 的所有因子优化计算
- 注意数据范围,使用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)
- 时间复杂度:\(\mathcal{O}(n \cdot K \cdot \sqrt{A})\) - 其中\(\sqrt{A}\)是A的因子个数的近似值
- 空间复杂度:\(\mathcal{O}(A \cdot n)\) - 需要存储动态规划数组

浙公网安备 33010602011771号