CF1620F Bipartite Array

不难发现本题的图在随机的情况下会很稠密,并且很容易出现奇环。仔细想想,会发现奇环出现的充要条件应该并不复杂。
考察最小的情况,也即长度为 \(3\) 的奇环,出现这样的环意味着有 \(i<j<k\) \(p_i>p_j>p_k\)。 仔细想一想,任何一个奇环都会包含这样三个位置 \(i,j,k\) 那么问题就更加简单化了,至此问题被转换为,是否能通过对于每个位置取反的操作使原数列不能出现满足 \(i<j<k\)\(p_i>p_j>p_k\) 的这样 \((i,j,k)\) 三元组。
先做一个 naive 的 \(DP\), 设 \(f_{i,x,y}\) 表示序列最大数为 \(x\),所有逆序对末尾数最大为 \(y\),能否存在长度为 \(3\) 的奇环。
转移 \(O(1)\) 状态 \(O(n^3)\) 考虑优化状态。

不难发现,将 \(y\) 一维去掉只记 \(f_{i,x}\) 表示构造完前 \(i\) 个数后形成的最大数为 \(x\) 的序列,逆序对末尾的最小数为 \(f_{i,x}\) 即可。因为对于相同的 \(x\)\(y\) 越小显然越好。

看上去似乎这个无法再进一步优化状态,我们尝试写下状态转移方程:
\(z = \pm p_{i+1}, f_{i,x}=y (z\geq y)\) (显然我们只能使得 \(z \geq y\)) 则我们有:

  1. \(z<x \Rightarrow f_{i+1,x}=z\)
  2. \(z\geq x \Rightarrow f_{i+1,z}=y\)
    那么我们只需要记 \(f_{i,j,k}\) 代表前 \(i\) 位, 第 \(i\) 位, 第 \(i\) 位填的是 正/负 \((j = 0/1)\),其中 \(x\) \(or\) \(y = \pm p_{i}\) \((k = 0/1)\)

Tips:
限制比较苛刻的构造题,可以考虑是否所有小情况满足就能合法。
\(DP\) 题优化状态时需要考察转移方程。

#include<bits/stdc++.h>
#define RG register
#define LL long long
#define U(x, y, z) for(RG int x = y; x <= z; ++x)
#define D(x, y, z) for(RG int x = y; x >= z; --x)
#define update(x, y) (x = x + y >= mod ? x + y - mod : x + y)
using namespace std;
void read(){}
template<typename _Tp, typename... _Tps>
void read(_Tp &x, _Tps &...Ar) {
	x = 0; char ch = getchar(); bool flg = 0;
	for (; !isdigit(ch); ch = getchar()) flg |= (ch == '-');
	for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
	if (flg) x = -x;
	read(Ar...);	
}
inline char Getchar(){ char ch; for (ch = getchar(); !isalpha(ch); ch = getchar()); return ch;}
template <typename T> inline void write(T n){ char ch[60]; bool f = 1; int cnt = 0; if (n < 0) f = 0, n = -n; do{ch[++cnt] = char(n % 10 + 48); n /= 10; }while(n); if (f == 0) putchar('-'); for (; cnt; cnt--) putchar(ch[cnt]);}
template <typename T> inline void writeln(T n){write(n); putchar('\n');}
template <typename T> inline void writesp(T n){write(n); putchar(' ');}
template <typename T> inline void chkmin(T &x, T y){x = x < y ? x : y;}
template <typename T> inline void chkmax(T &x, T y){x = x > y ? x : y;}
template <typename T> inline T Min(T x, T y){return x < y ? x : y;}
template <typename T> inline T Max(T x, T y){return x > y ? x : y;}
inline void readstr(string &s) { s = ""; static char c = getchar(); while (isspace(c)) c = getchar(); while (!isspace(c)) s = s + c, c = getchar();}
inline void FO(string s){freopen((s + ".in").c_str(), "r", stdin); freopen((s + ".out").c_str(), "w", stdout);}

const int N = 1e6 + 10;
#define ep emplace_back
int n, p[N], f[N][2][2];
vector<int> ans;
#define inf 2e9
struct node {
	int i, j, k;
} pre[N][2][2];

inline void solve() {
	read(n);
	U(i, 1, n) read(p[i]);
	U(i, 0, n + 1) U(j, 0, 1) U(k, 0, 1) f[i][j][k] = inf;
	f[1][0][0] = f[1][1][0] = -inf;
	U(i, 1, n - 1) U(j, 0, 1) U(k, 0, 1) {
		int x, y, tmp = f[i][j][k];
		if (!j && !k) x = -p[i], y = tmp;
		if (!j && k)  x = tmp, y = -p[i];
		if (j && !k) x = p[i], y = tmp;
		if (j && k) x = tmp, y = p[i];
		node cur = (node) {i, j, k};
		U(t, 0, 1) {
			int z = (t ? 1 : -1) * p[i + 1];
			if (z < y) continue ;
			if (z < x && f[i + 1][t][1] > x)
				f[i + 1][t][1] = x, pre[i + 1][t][1] = cur;
			if (z >= x && f[i + 1][t][0] > y)
				f[i + 1][t][0] = y, pre[i + 1][t][0] = cur;
		}
	}
	U(bj, 0, 1) U(bk, 0, 1) {
		int i = n, j = bj, k = bk;
		if (f[i][j][k] != inf) {
			ans.clear();
			puts("YES");
			while (i) {
				ans.emplace_back(j ? p[i] : -p[i]);
				node cur = pre[i][j][k];
				i = cur.i, j = cur.j, k = cur.k;
			}
			reverse(ans.begin(), ans.end());
			for (auto u: ans) writesp(u);
			putchar('\n');
			return ;
		}
	}
	puts("NO");
}

int main(){
	//FO("");
	int T;
	read(T);
	while (T--) solve();
	return 0;
}
posted @ 2022-11-08 23:24  Southern_Way  阅读(20)  评论(0)    收藏  举报