APIO2020 粉刷牆壁
阿新 • • 發佈:2020-08-18
觀察到 \(\sum{f(k)}^2\le 4\times 10^5\),我們可以暴力求出哪些長度為 \(M\) 的區間能夠被粉刷。
然後動態規劃求出最小代價:
- 設 \(dp_i\) 表示粉刷 \(1\) ~ \(i\) 的所有格子的最小代價。
- 轉移方程 \(dp_i =
\begin{cases}
+\infty, & \text{若以 }i\text{ 為結尾的長度為 }M\text{ 的區間不能夠被粉刷}\\
\min\limits_{j=i-M}^{i-1}dp_j+1, & \text{若以 }i\text{ 為結尾的長度為 }M\text{ 的區間能夠被粉刷}
\end{cases}\)
顯然這個 dp 可以使用單調佇列優化,其複雜度為 \(O(n)\)。
總複雜度 \(O(nf(k)\log f(k))\),可以通過此題。
#include "paint.h" #include <bits/stdc++.h> const int MAXN = 1e5 + 19, MAXM = 5e4 + 19; int Lp[MAXN], Rp[MAXN]; std::vector<int> L[MAXM], R[MAXM]; bool find(const std::vector<int> &b, int c){ auto it = std::lower_bound(b.begin(), b.end(), c); if(it != b.end() && *it == c) return 1; return 0; } int rec[MAXN]; int dp[MAXN], q[MAXN], head = 1, tail; int minimumInstructions (int N, int M, int K, std::vector<int> C, std::vector<int> A, std::vector<std::vector<int>> B){ for(int i = 0; i < M; ++i){ std::sort(B[i].begin(), B[i].end()); B[i].resize(std::unique(B[i].begin(), B[i].end()) - B[i].begin()); } for(int i = 0; i < N; ++i) if(find(B[0], C[i])) L[0].emplace_back(i), R[0].emplace_back(i); std::memset(Lp, -1, sizeof Lp), std::memset(Rp, -1, sizeof Rp); for(int i = 0; i < M; ++i){ if(L[i].empty() && R[i].empty()) break; for(auto j : L[i]) Lp[j] = i; for(auto j : R[i]) Rp[j] = i; if(i != M - 1){ for(auto j : L[i]){ if(j - i - 1 < 0) continue; if(find(B[M - i - 1], C[j - i - 1])) L[i + 1].emplace_back(j); } for(auto j : R[i]){ if(j + i + 1 > N - 1) continue; if(find(B[i + 1], C[j + i + 1])) R[i + 1].emplace_back(j); } } } for(int i = 0; i < N; ++i) if(Lp[i] != -1){ int l = i - Lp[i] + M - 1, r = i + Rp[i]; l = std::max(l, 0); if(r >= l) ++rec[l], --rec[r + 1]; } for(int i = 1; i < N; ++i) rec[i] += rec[i - 1]; if(!rec[N - 1]) return -1; for(int i = 0; i < M; ++i) if(rec[i]){ while(head <= tail && q[head] < i - M) ++head; dp[i] = 1; while(head <= tail && dp[q[head]] >= dp[i]) --tail; q[++tail] = i; } for(int i = M; i < N; ++i) if(rec[i]){ while(head <= tail && q[head] < i - M) ++head; if(head <= tail) dp[i] = dp[q[head]] + 1; else dp[i] = 0x3f3f3f3f; while(head <= tail && dp[q[head]] >= dp[i]) --tail; q[++tail] = i; } if(dp[N - 1] >= 0x3f3f3f3f) return -1; else return dp[N - 1]; }
還有另外一種使用 std::bitset
的 \(O(\dfrac {nm} {\omega})\) 做法,據說賽時被卡了空間,在此不做敘述。