SM 2020.9.9
阿新 • • 發佈:2020-09-14
T1
T2
設 \(f(i)\) 表示 \([1,i]\) 被凍住,\((i,n]\) 未被凍住的最少次數。
則 \(f(i) = \min \begin{cases} \infty & s_i = \texttt{1} \\ f(i-1) + i & s_i = \texttt{0}\\ \min\limits_{j\in [i-2k-1,i)}\{f(j)\} + i-k & s_{i-k} = \texttt{1} \end{cases}\) 。
單調佇列維護一下就是 \(O(n)\) 了。
注意要做到 \(f(n+k)\) ,對於 \(i \in (n, n+k]\)
則 \(ans=\min\limits_{i\in[n,n+k]} \{f(i)\}\) 。
#include <cstdio> #include <cstring> #include <algorithm> #define MAX_N (5000000 + 5) using std::memset; using std::min; using std::max; int T; int n, k; char s[MAX_N]; long long f[MAX_N]; int q[MAX_N], l, r; long long ans; int main() { scanf("%d", &T); while (T--) { memset(f, 0x3f, sizeof f); f[0] = 0; l = r = 1; q[l] = 0; scanf("%d%d%s", &n, &k, s + 1); for (int i = 1; i <= n; ++i) { if (q[l] + k + k + 1 < i) ++l; if (i - k > 0 && s[i - k] == '1') f[i] = f[q[l]] + i - k; if (s[i] == '0') f[i] = min(f[i], f[i - 1] + i); if (i - k - 1 > 0) { while (l <= r && f[q[r]] >= f[i - k - 1]) --r; q[++r] = i - k - 1; } } ans = f[n]; for (int i = n + 1; i <= n + k; ++i) { if (q[l] + k + k + 1 < i) ++l; if (i - k > 0 && s[i - k] == '1') ans = min(ans, f[q[l]] + i - k); if (i - k - 1 > 0) { while (l <= r && f[q[r]] >= f[i - k - 1]) --r; q[++r] = i - k - 1; } } printf("%lld\n", ans); } return 0; }
其實不用單調佇列,設 \(f(i)\) 表示 \([1,i]\) 被凍住,\((i,n]\) 未知的最少次數,考慮 \(f(i)\) 的單調性,對於每個 \(f(i)\) ,設 \(p_i\) 為左邊距離最遠且可覆蓋到 \(i\) 的禁咒的位置,直接取 \(f(p_i-k-1)\) 即可。
即 \(f(i) = \min \begin{cases} f(i-1) + i & \\ f(\min\{0, p_i - k - 1\}) + p_i & p_i \text{ exists.} \end{cases}\)
#include <cstdio> #include <cstring> #include <algorithm> #define MAX_N (5000000 + 5) using std::memset; using std::min; using std::max; int T; int n, k; char s[MAX_N]; long long f[MAX_N]; int p[MAX_N]; long long ans; int main() { scanf("%d", &T); while (T--) { memset(p, 0, sizeof p); scanf("%d%d%s", &n, &k, s + 1); int tmp = 1; for (int i = 1; i <= n; ++i) { if (s[i] == '0') continue; if (tmp < i) tmp = i; while (tmp <= n && tmp <= i + k) p[tmp++] = i; } for (int i = 1; i <= n; ++i) { f[i] = f[i - 1] + i; if (p[i]) f[i] = min(f[i], f[max(0, p[i] - k - 1)] + p[i]); } printf("%lld\n", f[n]); } return 0; }