今天我们将在豆包MarsCode AI刷题平台上,完成《小S的子序列平均数之和》和 《小U的数列因子挑战》算法问题, 都是考验的数学计算逻辑, 一起看看吧
小S的子序列平均数之和
分析
- 要知道不同的子序列可能, 对于i处,它能形成长度为l的子序列可以通过i-1形成长度为l-1的子序列去计算, 使用DP动态规划解决最适合不过了:
dp[i] = dp[i-1]+ arr[i] //长度为i的子序列的总和, 后面统一除以i,方便计算
- 根据样例2我们知道, 需要快速求得x能够 xb (mod p) =a , 转化为: x = ab^(-1) (mod p)
- 根据费马小定理: 如果p是一个质数,而整数a不是p的倍数,则有 a^(p-1)=1 (mod p)
- 得到x = a * b^(p-2) (mod p)
具体实现
public static long solution(int n, int[] arr) {
int mod = 1000000007;
long result = 0;
//记录当前处节点为末尾, 长度为i的 [i][0]平均值总和 [i][1]可能的情况数总和
long[][] dp = new long[n + 1][2];
dp[0][1] = 1;//长度为0的可能有一种
for (int i = 1; i <= n; i++) {
for (int j = i; j > 0; j--) {
dp[j][0] = (dp[j][0] + dp[j - 1][0] + arr[i - 1] * dp[j - 1][1]) % mod;
dp[j][1] += dp[j - 1][1];
}
}
for (int i = 1; i <= n; i++) {
//考虑浮点数的问题, 如例子2
result = (result + dp[i][0] / i) % mod;
if (dp[i][0] % i != 0) {
//x*b= a (mod p) => x = a/b (mod p)
//通过费马小定理优化 : 如果p是一个质数,而整数a不是p的倍数,则有 a^(p-1)=1 (mod p)
//这里需要求得 b^(-1) (mod p) = b^(p-2) (mod p) 至于如何求得b^(p-2) 快速幂
long val = 1;
long base = i;
int exp = mod - 2; // 根据费马小定理
while (exp > 0) {
if (exp % 2 == 1) {
val = (val * base) % mod;
}
base = (base * base) % mod;
exp >>= 1;
}
result = (result + (dp[i][0] % i) * val) % mod;
}
}
return result;
}
如果对费马小定理不了解, 可以使用下面代码替代
long val = mod + dp[i][0] % i;
while (val % i != 0) {
val += mod;
}
result = (result + val / i) % mod;
小U的数列因子挑战
分析
- 题目中的 c^d 并非代表异或, 而是c的d次方!!!
- 总结归纳. 可以得到第n项时, 使用a的个数, b的个数以及c^d的个数
- 对a,b,c因式分解. 相同的质因子共同计数, 能够获取到不同质因子的个数, 题目就回到了多个质因子不同的数量,有多少种取值
- 质因子k的数量为h 那么它的挑选可能为h+1 (+1代表都不选), 那么所有可能为 (h1+1)(h2+1)(h3+1).. -1, 最终减去1,代表不能每个因子都不选
具体实现
public static int solution(int a, int b, int c, int d, int n) {
//相当于是将a, b, c 因式分解. 等到不同个数的因子
int mod = 1000000007;
//对a进行因式分解
Map<Integer, Integer> factor; //质因子,不包含1
if (n == 1) {
factor = metaFactor(a);
} else if (n == 2) {
factor = metaFactor(b);
} else {
factor = new HashMap<>();
Map<Integer, Integer> factorA = metaFactor(a);
Map<Integer, Integer> factorB = metaFactor(b);
Map<Integer, Integer> factorC = metaFactor(c);
int countA = 0;
int countB = 1;
int countC = 0; //c的d次方
//前一个值
int _countA = 1;
int _countB = 0;
int _countC = 0;
int tmp;
for (int i = 3; i <= n; i++) { //归纳总结下就知道
tmp = _countA;
_countA = countA;
countA = countA + tmp;
tmp = _countB;
_countB = countB;
countB = countB + tmp;
tmp = _countC;
_countC = countC;
countC = countC + tmp + 1;
}
int t_countA = countA;
factorA.forEach((k, v) -> factor.put(k, v * t_countA + factor.getOrDefault(k, 0)));
int t_countB = countB;
factorB.forEach((k, v) -> factor.put(k, v * t_countB + factor.getOrDefault(k, 0)));
int t_countC = d * countC; //这里的c^d 是代表c的d次方. 不是异或
factorC.forEach((k, v) -> factor.put(k, v * t_countC + factor.getOrDefault(k, 0)));
}
// 有m个x0 有m1个x1.....有mi个xi , 在这些个质因子中随意选择. 但每组不能重复 x0>1
long result = 1; //因子为1
if (!factor.isEmpty()) {
//不包含1的质因子的选择的可能性
long multi = 1;
for (Integer value : factor.values()) {
multi = (multi * (value + 1)) % mod;
}
multi--;//减去1 (每个因子至少选择一种)
result = (result + multi) % mod;
}
return (int) result;
}
private static Map<Integer, Integer> metaFactor(int v) {
Map<Integer, Integer> factor = new HashMap<>();
int i = 2;
while (i * i <= v) {
int count = 0;
while (v % i == 0) {
count++;
v /= i;
}
factor.put(i, count);
i++;
}
if (v > 1) {
factor.put(v, 1);
}
return factor;
}
这道题不能单纯的算最终值, 因为fn一旦超过1000000007,那么取模后的因子数就变化了