题目
点这里看题目。
称一个数组是纯的,当且仅当其中不存在重复元素。
对于两个长度均为 \(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;
}
标签:std,Burenka,le,const,val,10,CF1718D,Permutation,include
From: https://www.cnblogs.com/crashed/p/16990573.html