给出一个 1 到 n 的排列 a ,我们将每个数字看做一个点,如果存在 a[i]>a[j] 且 i<j ,则在 (i,j) 之间连一条边。
最终如果得到的图为二分图,那么我们称 a 为二分图排列。二分图排列很稀有,因此给你一些机会,你可以选择 a 中的一些数字,把他的值变为 a[i] 的相反数。
问能否通过这样的修改,使得 a 变为二分图排列?如果可以,请输出合法的修改方案,如果有多种方案,请输出字典序最小的。
对于100%的数据,1≤t≤2000,1≤n≤1e6,∑n≤1e6。
修改后不存在长为3的下降序列,否则会出现奇环。
a中元素两两不同,不存在长为3的下降序列⇔可以划分为2个上升序列。
考虑dp, f[i][1/0]表示考虑i->n这一段,一个上升序列最开头为±a[i],另一个序列开头的最大值。
f[i][0/1]可由f[i + 1][0/1], ±a[i + 1]四个数转移而来。
维护字典序最小的方案需要从前向后结合dp结果贪心,分别记录2个上升序列的最后一个数值b, c.
比较b, c, ±a[i], f[i][1/0]的大小关系判断是否能拼接。
代码如下:
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f = ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
const int N = 1e6 + 10;
int n, a[N], f[N][2];
void work() {
n = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for