观察到 \(\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})\) 做法,据说赛时被卡了空间,在此不做叙述。