「CF1718D」Permutation for Burenka

题目

点这里看题目。


称一个数组是纯的,当且仅当其中不存在重复元素。

对于两个长度均为 \(n\) 的纯数组 \(a,b\),称它们是相似的,当且仅当:

\[\forall 1\le l\le r\le n,\arg \max_{l\le i\le r}a_i=\arg\max_{l\le j\le r}b_j \]

也即是,任意子区间内,最大值的位置相同。

现在,给定一个 \(1\sim n\) 的排列 \(p\) 和一个长度为 \(n\) 的数组 \(a\)\(a\) 中有一些位置是正整数,表示它们已经被确定;其它位置都是 \(0\),表示它们都还没有确定。设 \(k\)\(a\)\(0\)​ 的个数,保证 \(k\ge 2\)

给定一个大小为 \(k-1\)​​ 的集合 \(S\)​​ 和询问次数 \(q\)​​。每次询问中,你会得到一个正整数 \(x\)​​,并判断将 \(S\cup \{x\}\)​​ 中的数填入 \(a\)​​​ 中空位后(每个数必须恰好用一次,每个空位必须填上恰好一个数),得到的新数组 \(a'\)​ 是否能够与 \(p\)​ 相似。特别地,所有询问都保证得到的 \(a'\) 一定是一个纯数组

单个测试点内有多组测试数据。

所有测试点满足 \(1\le \sum n,\sum q\le 3\times 10^5,k\ge 2;1\le a_i,x\le 10^6;\forall x\in S,1\le x\le 10^6\)​​。

分析

首先进行翻译。根据 \(\arg\max\)​ 这一条,容易想到将“相似”的判断和笛卡尔树联系起来。具体来说,我们可以猜想两个纯数组相似 \(\Leftrightarrow\) 它俩笛卡尔树完全相同

这一条证明不难。\(\Leftarrow\) 显然,而 \(\Rightarrow\) 可以用相似的关系,得到每个元素左右两侧第一个大于它的位置,有了这个信息就可以直接构建笛卡尔树。

由于 \(p\)​​ 的笛卡尔树是确定的,所以我们把结构套过来,就可以知道 \(a\)​​ 上每个元素需要满足的条件。首先,先检查已经确定的元素是否满足条件。其次,已经确定的元素可以将待定元素限制在一个区间内(注意不是一个值域的后缀!);而待定元素之间的偏序关系,可以像“冒泡排序”(指 NOI2022)那样调整,所以不需要考虑。

所以,如果 \(a_i\)​ 待定,我们就设 \(a_i\)​ 的值区间为 \([l_i,r_i]\)​。之后的问题,实质上是一个二分图匹配,而如果 \(q=1\) 则有一个朴素的 \(O(n\log n)\)​ 的贪心可供使用。怎么用它处理多次询问?

考虑先对 \(S\)​ 中的元素跑一次贪心,则此时我们相当于有一个残量网络(尽管是隐式的),其流量必须为 \(k-1\)。当我们加入 \(x\) 之后,我们只需要跑出来一条增广路即可。注意到 \(q\)​ 次询问的增广路的终点一定是同一个尚未被匹配的值区间,我们可以从这个值区间开始,倒着 BFS 出来。这样可以到达的数就可以增广,不能到达的就不能增广,于是就可以对于 \(q\) 次询问进行判定了。

虽然有区间连边,但是因为是 BFS,所以不需要数据结构优化建图,用个并查集就够了。复杂度为 \(O(n\log n+q)\)

代码

#include <queue>
#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int INF = 1e9;
const int MAXN = 3e5 + 5;

template<typename _T>
inline void Read( _T &x ) {
	x = 0; char s = getchar(); bool f = false;
	while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	if( f ) x = -x;
}

template<typename _T>
inline void Write( _T x ) {
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) Write( x / 10 );
	putchar( x % 10 + '0' );
}

template<typename _T>
inline _T Max( const _T &a, const _T &b ) {
	return a > b ? a : b;
}

template<typename _T>
inline _T Min( const _T &a, const _T &b ) {
	return a < b ? a : b;
}

typedef std :: pair<int, int> Interval;

std :: priority_queue<Interval, std :: vector<Interval>, std :: greater<Interval> > lefQ;

std :: vector<int> itvLef[MAXN << 1];

int q[MAXN << 1];
int mat[MAXN << 1], fa[MAXN << 1];

bool imp[MAXN << 1];
int val[MAXN << 1], len = 0;

int seq[MAXN], tot = 0;
int arg[MAXN];

int stk[MAXN], top = 0;
int perm[MAXN], fixed[MAXN];
int lef[MAXN], rig[MAXN];
int lch[MAXN], rch[MAXN];

bool flg = true;
int N, Q;

inline void MakeSet( const int &n ) {
	rep( i, 1, n ) fa[i] = i;
}

int FindSet( const int &u ) {
	return fa[u] == u ? u : ( fa[u] = FindSet( fa[u] ) );
}

inline void UnionSet( const int &u, const int &v ) {
	fa[FindSet( u )] = FindSet( v );
}

void DFS( const int &u, int low ) {
	lef[u] = fixed[u], rig[u] = low;
	if( fixed[u] ) low = Min( low, fixed[u] );
	if( lch[u] ) DFS( lch[u], low ), lef[u] = Max( lef[u], lef[lch[u]] );
	if( rch[u] ) DFS( rch[u], low ), lef[u] = Max( lef[u], lef[rch[u]] );
}

inline void Discrete() {
	len = 0;
	rep( i, 1, Q ) val[++ len] = arg[i];
	rep( i, 1, tot - 1 ) val[++ len] = seq[i];
	std :: sort( val + 1, val + 1 + len );
	len = std :: unique( val + 1, val + 1 + len ) - val - 1;
	rep( i, 1, Q ) arg[i] = std :: lower_bound( val + 1, val + 1 + len, arg[i] ) - val;
	rep( i, 1, tot - 1 ) seq[i] = std :: lower_bound( val + 1, val + 1 + len, seq[i] ) - val;
}

int main() {
	int T; Read( T );
	while( T -- ) {
		Read( N ), Read( Q ), tot = 0;
		rep( i, 1, N ) Read( perm[i] ), lch[i] = rch[i] = 0;
		rep( i, 1, N ) Read( fixed[i] ), tot += ! fixed[i];
		rep( i, 1, tot - 1 ) Read( seq[i] );
		rep( i, 1, Q ) Read( arg[i] );

		top = 0;
		rep( i, 1, N ) {
			while( top && perm[stk[top]] <= perm[i] ) lch[i] = stk[top --];
			if( top ) { rch[stk[top]] = i; } stk[++ top] = i;
		}
		Discrete(), DFS( stk[1], 1e9 );
		rep( i, 1, len ) imp[i] = false, itvLef[i].clear(), mat[i] = 0;
		rep( i, 1, tot - 1 ) imp[seq[i]] = true;

		flg = true;
		rep( i, 1, N ) {
			if( lef[i] > rig[i] ) { flg = false; break; }
			if( fixed[i] ) continue;
			lef[i] = std :: upper_bound( val + 1, val + 1 + len, lef[i] ) - val;
			rig[i] = std :: lower_bound( val + 1, val + 1 + len, rig[i] ) - val - 1;
			if( lef[i] > rig[i] ) { flg = false; break; }
			if( lef[i] > len || rig[i] <= 0 ) { flg = false; break; }
			itvLef[lef[i]].push_back( i );
		}
		if( ! flg ) {
			rep( i, 1, Q ) puts( "NO" );
			continue;
		}

		while( ! lefQ.empty() ) lefQ.pop();
		int lost = -1;
		rep( i, 1, len ) {
			for( const int &x : itvLef[i] )
				lefQ.push( { rig[x], x } );
			if( ! imp[i] ) continue;
			while( ! lefQ.empty() && lefQ.top().first < i ) {
				if( ~ lost ) { flg = false; break; }
				lost = lefQ.top().second, lefQ.pop();
			}
			if( ! flg || lefQ.empty() ) { flg = false; break; }
			mat[i] = lefQ.top().second, lefQ.pop();
		}
		while( ! lefQ.empty() ) {
			if( ~ lost ) { flg = false; break; }
			lost = lefQ.top().second, lefQ.pop();
		}
		if( ! flg ) {
			rep( i, 1, Q ) puts( "NO" );
			continue;
		}
		MakeSet( len + 1 );
		int h = 1, t = 0; q[++ t] = lost;
		while( h <= t ) {
			int u = q[h ++];
			for( int x = FindSet( lef[u] ) ; x <= rig[u] ; x = FindSet( x + 1 ) ) {
				UnionSet( x, x + 1 );
				if( mat[x] ) q[++ t] = mat[x];
			}
		}
		rep( i, 1, Q ) puts( fa[arg[i]] != arg[i] ? "YES" : "NO" );
	}
	return 0;
}
posted @ 2022-12-18 16:58  crashed  阅读(43)  评论(0编辑  收藏  举报