TLE Time Limit Exceeded 高維字首和
阿新 • • 發佈:2019-02-13
題意:給出長度為n的序列c,求非負整數序列a,滿足a<2^m,並且有a[i]&a[i+1]=0,對於每個a[i],要保證a[i]不是c[i]的倍數,求這樣的a[i]序列的個數
思路:dp[i][j]表示長度為i以j結尾的序列的個數,可以第一個條件和第三個條件都好處理,關鍵是第二個,可以發現dp[i][j] = ∑(dp[i - 1][k] * (k & j == 0)), 那麼可以轉化為~j & k == k,即k為~j的子集,也就是說dp[i][j] = dp[i
- 1][~j] = dp[i - 1][((1 << m) - 1) ^ j]. 所以每次我們算完
dp[i][1..n]後,要對每個dp[i][j]求j的所有子集的和,留作下一次轉移用。
dalao思路 + 精簡程式碼:點選開啟連結
程式碼:
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; typedef pair<int,int> P; const int MAXN = 100010; const int mod = 1e9; ll dp[MAXN], sum[MAXN]; int c[55]; int main() { int T, n, m; cin >> T; while(T--) { scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", c + i); int up = 1 << m; for(int i = 0; i < up; i++) { if(i % c[1]) dp[i] = sum[i] = 1; else dp[i] = sum[i] = 0; } for(int j = 0; j < m; j++) for(int k = 0; k < up; k++) if(k & 1 << j) sum[k] = (sum[k] + sum[k ^ (1 << j)]) % mod;//求k的子集的和 for(int i = 2; i <= n; i++) { for(int j = 0; j < up; j++) { if(j % c[i]) dp[j] = sum[(up - 1) ^ j]; else dp[j] = 0; } for(int j = 0; j < up; j++) sum[j] = dp[j]; for(int j = 0; j < m; j++) for(int k = 0; k < up; k++) if(k & 1 << j) sum[k] = (sum[k] + sum[k ^ (1 << j)]) % mod; } ll ans = 0; for(int i = 0; i < up; i++) ans = (ans + dp[i]) % mod; cout << ans << endl; } return 0; }
第一次做高維字首和的題,個人理解:高維字首和可以解決狀壓表示的一類集合之間的字首和問題,例如求某個集合所有子集對應的值的和,或者某個集合的所有超集和。
求超集和:
for(int i = 0; i < m; i++)
for(int j = 0; j < (1 << m); j++)
if(!(j & 1 << i))
dp[i] += dp[j | (1 << i)];
求子集和:
for(int j = 0; j < m; j++) for(int k = 0; k < (1 << m); k++) if(k & 1 << j) sum[k] = (sum[k] + sum[k ^ (1 << j)]) % mod;//求k的子集的和