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

上一篇:Odoo 12开发之看板视图和用户端 QWeb


下一篇:几何+矩形交——icpc nwerc 2019 I