周賽301。花了一個禮拜才一知半解,最大的收穫可能還是觀察出數列規則的方法,會不會做出排列組合反而不是重點。

題目

輸入兩個整數n和maxValue,用來生成理想陣列

若長度n的陣列arr滿足以下條件,則稱為是理想陣列

  • 每個arr[i]是一個從1到maxValue的值,0 <= i < n
  • 每個arr[i]都可以被arr[i-1]整除,0 < i < n

回傳長度為n的不同理想數組的數量。答案可能非很大,先模10^9+7後回傳。

解法

原本只想出單純的dp方法,對於長度為n,最末端數值為v的陣列,可以透過加總長度n-1且最末端數值為v因數求出。
複雜度為O(n*maxValue^2),若兩者代入1000,大概是10^9,當然是超時的。

class Solution:
    def idealArrays(self, n: int, maxValue: int) -> int:
        MOD=10**9+7
        
        @cache
        def dp(n,v):
            if n==1:
                return 1
            cnt=dp(n-1,v)
            for i in range(1,v//2+1):
                if v%i==0:
                    cnt+=dp(n-1,i)
            return cnt%MOD

        ans=0        
        for i in range(1,maxValue+1):
            ans=(ans+dp(n,i))%MOD
    
        return ans

參考解答提到,以數字x結尾的長度n陣列,會將x個質因數分散在n個位置中。
這相當於把問題拆分成x個隔板法子問題。

例如以8結尾,長度為4的陣列,要將質因數2,2,2塞在任意四個位置,所以有C(4+3-1,3)種放法。若有多種質因數,因為不會互相干擾,直接相乘即可。

不過質因數分解的方法也很重要,我自己寫的分解法只通過46/47測資,換成和題解一樣的寫法才過。

class Solution:
    def idealArrays(self, n: int, maxValue: int) -> int:
        MOD=10**9+7
        
        def prime(n):
            freq=[]
            p=2
            while p*p<=n:
                if n%p==0:
                    cnt=1
                    n//=p
                    while n%p==0:
                        cnt+=1
                        n//=p
                    freq.append(cnt)
                p+=1
            return freq+(n>1)*[1]
            
        ans=0
        for i in range(1,maxValue+1):
            num=1
            for k in prime(i):
                num=(num*comb(n+k-1,k))%MOD
            ans=(ans+num)%MOD
            
        return ans