cf577 B. Modulo Sum(抽屜原理/bitset優化01揹包)
阿新 • • 發佈:2022-03-01
題意:
給定n個數,問能否選出若干個數的,和為m的倍數
\(1\le n \le 1e6, 2\le m \le 1e3\)
思路:
法一:(取模的性質、抽屜原理、01揹包)
對原陣列做字首和並取模,可能的字首和只有 \([0,m-1]\),m種值。由抽屜原理,若 \(n\ge m\) 就一定有兩個字首和相等,所以一定可以找到和為m的倍數的子段。否則做一下普通的01揹包
const int N = 1e6 + 5, M = 1e3 + 5; int n, m; bool f[2][M]; //滾動 main() { cin >> n >> m; if(n > m) return cout << "YES", 0; for(int i = 1; i <= n; i++) { int x; cin >> x; x %= m; for(int j = 0; j < m; j++) if(f[(i-1)&1][j]) f[i&1][j] = f[i&1][(j+x)%m] = 1; f[i&1][x] = 1; } cout << (f[n&1][0] ? "YES" : "NO"); }
法二:(bitset優化01揹包)
如果沒發現上面的性質,直接做01揹包複雜度 \(O(nm)\) 會超時,bitset優化一下即可。複雜度少一個常數?
開個bitset表示能湊出來的數的集合。注意bitset的最右邊是第0位,最左邊是最高位。
對於某個數 x,可以只選x,或者把前面的方案全部加上x(即左移x位)。加x之後超過m的數要減小m,相當於左移x位再右移m位,即右移 m-x 位。
最後判斷 f[n][0]
是否為1
const int N = 1e6 + 5, M = 1e3 + 5; int n, m; bitset<M> f[2]; //滾動 main() { iofast; cin >> n >> m; if(n > m) return cout << "YES", 0; for(int i = 1; i <= n; i++) { int x; cin >> x; x %= m; f[i&1] = f[(i-1)&1] | (f[(i-1)&1] << x) | (f[(i-1)&1] >> (m-x)); f[i&1][x] = 1; //只選自己 } cout << (f[n&1][0] ? "YES" : "NO"); }