[BZOJ4069][Apio2015]巴厘島的雕塑
阿新 • • 發佈:2018-10-06
一個點 當前 天發 span 總結 滿足 區間 long oid 表示前 \(i\) 個數分成 \(j\) 段,是否存在這一種情況
如果滿足上述條件,我們就能夠從 \(f[k][j - 1]\) 轉移到 \(f[i][j]\) 沒辦法優化,結果發現最後一個點的 \(A = 1\)。
題目大意
分成 \(x\) 堆,是的每堆的和的異或值最小
分析
這是一道非常簡單的數位 \(DP\) 題
基於貪心思想,我們要盡量讓最高位的 \(1\) 最小, 因此我們考慮從高位向低位進行枚舉,看是否存在一種方案使得最高為不為 \(1\),如果不存在,那就填 \(1\)
因此我們要解決如下問題:
- 保存之前的狀態
- 考慮 \(A\), \(B\) 的限制
我們設已經考慮到 \(x\) 位,並且當前的值為 \(ans\)
顯然如果該位可以不填,它必須滿足存在一種狀態使得之前已經決定的狀態的異或和為 \(ans\), 並且新增的一段不會使得 \(ans\) 改變
那這就是一個經典的區間DP問題
我們不妨設 \(f[i][j]\)
如果滿足上述條件,我們就能夠從 \(f[k][j - 1]\) 轉移到 \(f[i][j]\)
對於每個一位,如果有一種給情況使得當前為為0,那就讓 \(ans\) 的當前位為 \(0\)
復雜度:\(O(n^3logD)\)
對於最後一組數據:\(A =1, B \le n\),段數只有上限
我們要想把 \(f\) 數組的第二個維度省掉的話,用 \(f[i]\) 記錄將前 \(i\) 個數分段並得到可行解的最小段數,最後判斷其是否小於 \(B\),即可
復雜度:\(O(n^2logD)\)
總結
想了半天發現 \(O(n^3logD)\)
const int maxn = 2018; typedef long long LL; LL s[maxn]; int f[maxn][maxn], g[maxn]; LL ans = 0, t; int n, A, B, len = 0; bool valid(LL val, int dig) { return ((val >> (LL)dig | ans) == ans && (val & 1LL << (LL)dig-1LL) == 0); } void work1() { for(int x = len; x > 0; -- x) { forn(i, n) g[i] = INF; forn(i, n) Forn(j, i) if (g[j] < B) { t = s[i] - s[j]; if(valid(t, x)) if (g[j] + 1 < g[i]) g[i] = g[j] + 1; } ans <<= 1LL; if(g[n] > B) ans ++; } } void work2() { for(int x = len; x > 0; -- x) { memset(f, 0, sizeof(f)); f[0][0] = 1; forn(i, n) forn(j, i) for (int k = 0; k < i; ++ k) if(f[k][j-1]) { t = s[i] - s[k]; //printf("x = %d i = %d j = %d k = %d t = %d\n", x, i, j, k, t); if(valid(t, x)) f[i][j] = 1; } int i; for(i = A; i <= B; ++ i) { if(f[n][i]) break; } ans <<= 1LL; if(i > B) ans ++; } } int main() { read(n, A, B); forn(i, n) { read(s[i]); s[i] += s[i-1]; } len = (int)log2(s[n]) + 1; if (A == 1) work1(); else work2(); printf("%lld", ans); return 0; }
[BZOJ4069][Apio2015]巴厘島的雕塑