1. 程式人生 > 實用技巧 >APIO2020 粉刷牆壁

APIO2020 粉刷牆壁

觀察到 \(\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})\) 做法,據說賽時被卡了空間,在此不做敘述。