# Leetcode 解題記錄 3539. Find Sum of Array Product of Magical Sequences🏚️ ### 題目大意: 給定一組int array : nums,以及int M和K 定義 seq : sequence * seq 大小為 M (從int array可重複取M次) * S = 2^(seq\[0\]) + 2^(seq\[1\]) + ... + 2^(seq\[M-1\]),S的二進位剛好有K個bits是1 * prod(seq) = (nums[seq[0]] * nums[seq[1]] * ... * nums[seq[m - 1]]) 求所有滿足上面條件的prod(seq)的總和 ### 解法思路: 題目的要求很明顯要搞組合問題,又是可重複的sequence第一直覺就是搞二項式或多項式自己乘自己,所以可以想像把nums放在生成函式f上,求f^m。再來是按照不同的S分類,最一開始就是 `[1:nums[0],2:nums[1],4:nums[2],8:nums[3],....,2^n-1:nums[n-1]]` 在觀察這些狀態轉移就是簡單的加法,所以f*f的結果可以將S相同的prod先加起來,操作會像是 ``` unordered_map<long long,long long> mul_states(unordered_map<long long,long long> &a,unordered_map<long long,long long> &b,int mod){ unordered_map<long long,long long> res; for(auto &as:a){ for(auto &bs:b){ long long nxt_state = as.first+bs.first; res[nxt_state]=(res[nxt_state]+as.second*bs.second)%mod; } } return res; } ``` 接下來再用fastpow的方式可以求出所有S的product在挑出set bits為K的加總 ``` int magicalSum(int M, int K, vector<int>& nums) { int mod = 1e9+7,n = nums.size(); unordered_map<long long,long long> states,pow_states; states[0] = 1; for(int i=0;i<n;i++){ pow_states[1ll<<i] = nums[i]; } int t = 1; while(M){ if(M&1){ states = mul_states(states,pow_states,mod); } if(M>1)pow_states=mul_states(pow_states,pow_states,mod); M>>=1; } long long ans = 0; for(auto &s:states){ if(__builtin_popcountll(s.first)==K){ ans+=s.second; ans%=mod; } } return ans; } ``` 但是這樣作狀態樹會爆,想到開始懷疑人生,心態炸裂,道心已毀,放棄思考。 解法留在下面,想不出來破大房的可以往下看 ![image](https://hackmd.io/_uploads/HyfnJS5exl.png) <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 這裡用一個方式去壓縮狀態,先前定義的狀態修改億點點就可以了。 先定義dp[i][m][k][b] 最簡單的來說如果我先把位置0的nums[0]出現的可能性都處理完那麼接下來S的最小的bit就固定了(因為之後不會再乘上nums[0],其他的狀態要加也是2,4,8,...,不會動到最小bits),所以這個最小bit可以用另一個dim紀錄,因為前一個i殘留的狀態最多是M/2,當前的狀態最多會是(M/2+M)/2\<M,所以狀態數是可以控制的。 * dp state中 * i 代表處理多少位置,同時也是k dim size的上限 * m 代表已經乘過幾個數字,最多是M * k 代表處理過的bits有幾個是1 * b 有可能因為後續操作而改變的bits state 整個狀態轉移就會像是 //從狀態dp[i-1][m][k][b],第i個元素取q次 dp[i][m+q][k+((b+q)&1)][(b+q)>>1] += dp[i-1][m][k][b]*(...); (...) : inverse of q! under modulo 1e9+7 (排列組合先除,之後一起乘n!) 最後在挑出dp[n-1][K]中符合的部分加起來,乘上n! (mod 1e9+7) 簡單舉個幾個例子: ``` seq = [1,2,1,3], S = 16 用dp的方式則會記錄在下面的路徑中 dp[0][0][0][0] -> dp[0][2][0][1] -> dp[1][3][0][1] -> dp[2][4][0][1] S = 0 -> 4 -> 8 -> 16 seq = [1,0,2,2], S = 11 dp[0][1][1][0] -> dp[0][2][2][0] -> dp[1][4][2][1] -> dp[2][4][3][0] S = 1 -> 3 -> 11 -> 11 ``` ### Murmur 解了不少hard problem,大部分想一下都可以解,要看hint的也不到十題,這題是第一題我看hint還看不懂到去看解答的,整個大破房,一開始的方向錯了彎不過來,so sad。