题目链接
给你两个整数 n
和 maxValue
,用于描述一个 理想数组 。
对于下标从 0
开始、长度为 n
的整数数组 arr
,如果满足以下条件,则认为该数组是一个 理想数组 :
arr[i]
都是从 1
到 maxValue
范围内的一个值,其中 0 <= i < n
。arr[i]
都可以被 arr[i - 1]
整除,其中 0 < i < n
。返回长度为 n
的 不同 理想数组的数目。由于答案可能很大,返回对 1 0 9 + 7 10^{9} + 7 109+7 取余的结果。
示例 1:
输入:n = 2, maxValue = 5
输出:10
解释:存在以下理想数组:
- 以 1 开头的数组(5 个):[1,1]、[1,2]、[1,3]、[1,4]、[1,5]
- 以 2 开头的数组(2 个):[2,2]、[2,4]
- 以 3 开头的数组(1 个):[3,3]
- 以 4 开头的数组(1 个):[4,4]
- 以 5 开头的数组(1 个):[5,5]
共计 5 + 2 + 1 + 1 + 1 = 10 个不同理想数组。
示例 2:
输入:n = 5, maxValue = 3
输出:11
解释:存在以下理想数组:
- 以 1 开头的数组(9 个):
- 不含其他不同值(1 个):[1,1,1,1,1]
- 含一个不同值 2(4 个):[1,1,1,1,2], [1,1,1,2,2], [1,1,2,2,2], [1,2,2,2,2]
- 含一个不同值 3(4 个):[1,1,1,1,3], [1,1,1,3,3], [1,1,3,3,3], [1,3,3,3,3]
- 以 2 开头的数组(1 个):[2,2,2,2,2]
- 以 3 开头的数组(1 个):[3,3,3,3,3]
共计 9 + 1 + 1 = 11 个不同理想数组。
提示:
算法1: dp
dp[ i][ j]
为: 前i
个元素, 最后元素为j
; 每次遍历他的约数
时间复杂度是: O(n * m * sqrt(m))
, 超时
算法2: 优化dp
dp[ i][ j]
为: 前i
个元素, 最后元素是: 所有j的约数
, 每次以当前dp去更新dp[ i][ j的倍数]
时间是: O( n * m * log( m))
, 1e8 * 15
也是超时的! 不要碰侥幸心理.
算法3: 组合数学
当涉及到倍数
时, 要想到, 这是指数
量级, 递增速度是非常快的!
最大值是1e4
, 最多也就15
次倍数操作 1, 2, 4, 8, 16, 32, ...
因此, 虽然这个数组会很长, 但其unique
操作后, 最多也就15
个不同的数字
以12
来看, 其unique
后, 可能是[1, 2, 4, 12]
, [1, 3, 6, 12]
, [4, 12]
, [12]
, …
看其每次的乘积因子
:
[1, 2, 4, 12]
, 他的乘积因子
是: [2, 2, 3]
[1, 3, 12]
, 他的乘积因子
是: [3, 4]
其乘积因子
的积, 等于12
有一种特别的 [4, 12]
, 其乘积因子是: [3]
;
但如果把他的第一项加入, 即[4, 3
], 乘积也是12
定义一个序列A
的乘积因子B
为:
B[ 1] = A[ 1]
B[ i] = A[ i] / A[ i - 1], i > 1
类似于差分数组
.
对于一个长度为n
, 且最后元素为m
的 理想数组A, 其乘积因子为B
数组, 则有:
B[ i]
= 1
或 m的约数
A: [1, 2, 2, 4, 12]
-> B: [1, 2, 1, 2, 3]
A: [4, 4, 4, 4, 12]
-> B: [4, 1, 1, 1, 3]
A: [12, 12, 12, 12, 12]
-> B: [12, 1, 1, 1, 1]
问题转换为: 在B数组的n
个位置里, 放m
的非1
的约数, 其余位置全放1
的方案数.
由于一个数的约数
, 是有很多个的. 约数又可以分解为质因子, 而质因子很少, 从质因子角度来看
12的质因子 = 2, 2, 3
此时问题是: 将这3
个质因子, 放入B的这n
个位置里; 每个位置放>=0
个. 即小球与盒子模型
这n
个位置, 即盒子
, 是不同
盒子; 比如[空, 2]
和 [2, 空]
是不同的
而这些质因子呢? 都是相同小球
? 还是不同小球
?
比如: [2, 3]
和 [3, 2]
是不同的, 因此: 不同质因子, 是不同小球
但是: [2, 2]
和 [2, 2]
是相同的, 即: 相同质因子, 是相同小球
也就是, 对于相同的质因子
, 他们是相同小球
, 应该转换为: 相同小球
的 小球与盒子模型
那么, 不同质因子可以分开吗? 拆分成若干子问题, 每一个子问题即是**相同小球
的 小球与盒子模型**
由于不同质因子可以放到同一个
盒子里, 因此, 不同质因子 可以单独处理.
每个不同质因子的方案, 通过复合, 从而获得 原问题的方案. 这里的复合, 是满足乘法原理
的
如果不同小球不能放同一个
盒子, 则不能拆分; 否则:
比如, 如果拆分了 [2, 空]
和 [3, 空]
; 他俩复合为[23, 空]
, 该方案是非法的.
对于拆分后的子问题: a
个相同小球, 选出a
个, 放到b
个不同盒子里, 且每个盒子>=0
个
此为可空的隔板法, 即: C a + b − 1 b − 1 C_{a + b - 1}^{b - 1} Ca+b−1b−1
a = 15, b = 1e4
, 即所有组合数范围为: C[ 1e4][ 1e4]
看似无法预处理, 但是: 由于a很小, b很大, 运用组合数的等价式
C[ a + b - 1][ b - 1]
== C[ a + b - 1][ a]
即就转变为: C[ 1e4][ 15]
的量级!!! 可以预处理
#define GET_( _t, _i) ::std::get< _i>( _t)
#define FOR_( _i, _l, _r, _s) for( int _i = _l; _i <= _r; _i += _s)
#define FOR_RE_( _i, _l, _r, _s) for( int _i = _l; _i >= _r; _i -= _s)
class Solution {
public:
int P[ 100], P_size;
int C[ 10055][ 15]; //< 2^14 > 1e4
static constexpr int Mod_ = 1e9 + 7;
void init_C(){
C[ 0][ 0] = 0;
FOR_( i, 1, 10050, 1){
C[ i][ 0] = 1;
if( i <= 14){
C[ i][ i] = 1;
}
FOR_( j, 1, min( i - 1, 14), 1){
C[ i][ j] = (C[ i - 1][ j] + C[ i - 1][ j - 1]) % Mod_;
}
}
}
int get_C( int _a, int _b){
assert( _a >= _b);
if( ( _a - _b) < _b){
_b = _a - _b;
}
//* 要么逆元( C = a / b), 要么预处理(C = a + b)
//* 如果要直接求, 虽然他的结果是整数, 即(C = a / b), b一定可以消去, 但a很大, 会爆ll
return C[ _a][ _b];
}
int idealArrays(int _n, int _ma) {
init_C();
long long ans = 0;
ans ++; //< [1, ..., 1]
FOR_( i, 2, _ma, 1){ //< 1要特殊处理, 因为1无法质因数分解
P_size = 0;
int temp = i;
for( int j = 2; j <= temp / j; ++j){
if( temp % j == 0){
P[ P_size] = 0;
while( temp % j == 0){
++ P[ P_size];
temp /= j;
}
++ P_size;
}
}
if( temp > 1){
P[ P_size] = 1;
++ P_size;
}
//--
long long anss = 1;
FOR_( j, 0, P_size - 1, 1){
anss *= get_C( _n + P[ j] - 1, _n - 1);
anss %= Mod_;
}
ans += anss;
ans %= Mod_;
}
return ans;
}
};