LibreOJ #3271. 「JOISC 2020 Day1」建筑装饰 4

Description

给定长度为 $2n$ 的两个序列,分别为序列 $\text A$,和序列 $\text B$。

构造一个长度为 $2n$ 的序列 $\text C$。满足以下条件:

  • 序列 $\text C$ 的第 $i$ 个数 $C_i$,只能从 $A_i$ 和 $B_i$ 中选取;
  • 设 $a$ 为序列 $\text A$ 中元素被选取的次数,$b$ 为序列 $\text B$ 中元素被选取的次数,则 $a = b = n$;
  • 该序列是一个单调不下降的序列。

如有多解,任意输出一组解即可,无解输出 $-1$。

Solution

后文请自动将状态中的 $A$ 视作 $0$,$B$ 视作 $1$。 

$\mathcal O(n^2)$ 的思路还是很好想的,用 $f_{i, j, A/B}$ 表示前 $i$ 个数,选了 $j$ 个 $\text A$ 中的,最后一个数字选的是 $\text A$ 中的还是 $\text B$ 中的,当前末尾的最小值是多少。转移显然。

改变策略,用 $f_{i, A/B, A/B}$ 表示前 $i$ 个数,最后一个选了 $\text A$ 中的还是 $\text B$ 中的,最大化选 $\text A$ 的个数还是选 $\text B$ 的个数,最多能选多少个 $\text A / \text B$。

我们枚举 $i$ 和 $i - 1$ 的状态,分类讨论。下文的 $\to$ 可以视作「去更新」,也就是一个取 $\max$ 的操作。

  • 如果 $A_{i-1} \le A_i$,那么我们试图选择 $A_{i - 1}$ 和 $A_i$,这时可以用 $f_{i - 1, A, A} + 1 \to f_{i, A, A}$(又选了一个 $\text A$),用 $f_{i - 1, A, B} \to f_{i, A, B}$(没有选 $\text B$);
  • 如果 $B_{i-1} \le A_i$,那么我们试图选择 $B_{i - 1}$ 和 $A_i$,这时可以用 $f_{i - 1, B, A} + 1 \to f_{i, A, A}$(又选了一个 $\text A$),用 $f_{i - 1, B, B} \to f_{i, A, B}$(没有选 $\text B$);
  • 如果 $A_{i-1} \le B_i$,那么我们试图选择 $A_{i - 1}$ 和 $B_i$,这时可以用 $f_{i - 1, A, A} \to f_{i, B, A}$(没有选 $\text A$),用 $f_{i - 1, A, B} + 1 \to f_{i, B, B}$(又选了一个 $\text B$);
  • 如果 $B_{i-1} \le B_i$,那么我们试图选择 $B_{i - 1}$ 和 $B_i$,这时可以用 $f_{i - 1, B, A} \to f_{i, B, A}$(没有选 $\text A$),用 $f_{i - 1, B, B} + 1 \to f_{i, B, B}$(又选了一个 $\text B$)。

求出这个有什么作用呢?当 $f_{2n, A, A} \ge n$ 且 $f_{2n, A, B} \ge n$;或者当 $f_{2n, B, A} \ge n$ 且 $f_{2n, B, B} \ge n$ 时必然有解。这很显然,因为只要两者都能选 $\ge n$ 个,我们就让两者都选 $n$ 个好了。

然后逆序构造答案,时刻保证选 $\text A$ 的个数和选 $\text B$ 的个数都 $\ge n$ 就好了。

时间复杂度 $\mathcal O(n)$。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5, A = 0, B = 1; 
int n, a[N], b[N], f[N][2][2];
char ans[N];
inline void upd(int &x, int y) { if(y > x) x = y; }
int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n << 1; i++) scanf("%d", &a[i]);
	for(int i = 1; i <= n << 1; i++) scanf("%d", &b[i]);
	memset(f, -0x3f, sizeof f);
	f[1][A][A] = 1; f[1][A][B] = 0; f[1][B][A] = 0; f[1][B][B] = 1; 
	for(int i = 2; i <= n << 1; i++)
	{
		if(a[i - 1] <= a[i])
			upd(f[i][A][A], f[i - 1][A][A] + 1), upd(f[i][A][B], f[i - 1][A][B]);
		if(b[i - 1] <= a[i])
			upd(f[i][A][A], f[i - 1][B][A] + 1), upd(f[i][A][B], f[i - 1][B][B]);
		if(a[i - 1] <= b[i])
			upd(f[i][B][A], f[i - 1][A][A]), upd(f[i][B][B], f[i - 1][A][B] + 1);
		if(b[i - 1] <= b[i])
			upd(f[i][B][A], f[i - 1][B][A]), upd(f[i][B][B], f[i - 1][B][B] + 1);
	}
	int cntA = 0, cntB = 0, lst = INT_MAX;
	for(int i = n << 1; i; i--)
	{
		if(cntA + f[i][A][A] >= n && cntB + f[i][A][B] >= n && a[i] <= lst)
			cntA++, ans[i] = 'A', lst = a[i];
		else if(cntA + f[i][B][A] >= n && cntB + f[i][B][B] >= n && b[i] <= lst)
			cntB++, ans[i] = 'B', lst = b[i];
		else return puts("-1") && 0;
	}
	for(int i = 1; i <= n << 1; i++) putchar(ans[i]);
	return 0;
}
posted @ 2020-03-21 20:40  syksykCCC  阅读(426)  评论(0编辑  收藏  举报