本题可利用单调栈进行求解。
解法:
-
忽略 0 0 0值位,在从左到右扫描数组的过程中维护单调递增栈,如遇见栈顶元素比数组值大的情况,弹出栈直至栈顶元素比数组值小,并将该值压入栈内维持单调;
-
同时利用集合记录已弹出元素,如遇重复值尝试再次加入单调栈,则产生非法构造;
-
如维护单调栈过程合法,则视情况为 0 0 0值位填充邻近数组值、或操作数 q q q即可(需保证区间连续且最大值 q q q出现)。
原理:
由于操作对象为连续区间,若操作合法,数组内不允许出现小值区间覆盖大值区间的情况,也就是同一大值段区间不能被其他非零小值斩断。若单调栈已弹出元素再次申请入栈,则出现了斩断情况。如图例:
为理解方便,读者可以将题目的操作想象为堆土过程。同一高度的高土包由于覆盖时间更晚,区间内不可能出现更低的土坑。
代码如下:
#include <iostream>
#include <stack>
#include <set>
using namespace std;
#define LL long long
stack<int> Sta;
set<int> hasPoped;
const int maxn = 200000 + 10;
int a[maxn];
int main()
{
ios::sync_with_stdio(false);
bool valid = true;
bool qAppeared = false;
bool zeroAppeared = 0;
int n;
int q;
cin >> n >> q;
int lastZeroPos;
for (int i = 0; i < n; i ++){
cin >> a[i];
if (hasPoped.count(a[i])){
valid = false;
}
if (!valid){
continue;
}
if (a[i] == 0){
lastZeroPos = i;
zeroAppeared = true;
continue;
}
if (a[i] == q){
qAppeared = true;
}
while (Sta.size() && Sta.top() > a[i]){
hasPoped.insert(Sta.top());
Sta.pop();
}
Sta.push(a[i]);
}
if (!valid || (qAppeared == false && zeroAppeared == false)){
cout << "NO" << endl;
return 0;
}
cout << "YES" << endl;
for (int i = 0; i < n; i ++){
if (a[i] == 0){
a[i] = (i != 0 ? a[i-1] : 1);
}
}
if (!qAppeared){
a[lastZeroPos] = q;
}
for (int i = 0; i < n; i ++){
if (i) cout << " ";
cout << a[i];
}
cout << endl;
return 0;
}