Linking
大意:
给定一个长度为 n 的序列。
现在有 q 次操作,对于第 i 次:选定长度不为 0 的一段区间,把区间中的所有值变为 i。
现在对于给定的序列,判断是否由上述 q 次操作得到。
序列中可能有 0,0 可以变成 1~q 中的任意数。
思路:
注意几个点:
-
不存在中间的元素比两端的小(除去0): 单调栈,维护单调递增的栈。
遍历每个位置,如果该元素之前出现过,判断第一个小于等于该元素的值是不是和其相等。 如果不是,说明中间有比其小的,NO。 -
元素的最大值一定要为 q:
把一个 0 变为 q。如果没有 0,NO。 -
最后把 0 都变成相邻元素:
正着走,反着走,把 0 变为相邻元素。特判全为 0 情况。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m;
int a[N], b[N];
int main(){
Ios;
cin>>n>>m;
int flag=0, maxa=0;
for(int i=1;i<=n;i++) cin>>a[i];
stack<int> stk; //判断是否有中间比两端小的情况
for(int i=1;i<=n;i++)
{
if(a[i]==0) continue;
while(stk.size() && stk.top() > a[i]) stk.pop();
if(b[a[i]] && stk.top()!=a[i]){
cout<<"NO";return 0;
}
stk.push(a[i]);
b[a[i]]++;
}
for(int i=1;i<=n;i++) maxa=max(maxa,a[i]); //找到最大值
if(maxa==m) //最大值是m,把0都变成相邻的
{
for(int i=1;i<=n;i++)
if(a[i]==0) a[i]=a[i-1];
for(int i=n;i>=1;i--){
if(a[i]==0) a[i]=a[i+1];
}
for(int i=1;i<=n;i++) if(a[i]==0) a[i]=m;
cout<<"YES\n";
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}
else //最大值不是m,要让其中的一个0变为m,其余的变成相邻的
{
int flag=0;
for(int i=1;i<=n;i++)
{
if(a[i]==0 && !flag) flag=1, a[i]=m;
else if(a[i]==0) a[i]=a[i-1];
}
if(!flag){ //最大值不是m,还不存在0,无解
cout<<"NO";return 0;
}
for(int i=n;i>=1;i--)
if(a[i]==0) a[i]=a[i+1];
for(int i=1;i<=n;i++) if(a[i]==0) a[i]=m;
cout<<"YES\n";
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}
return 0;
}
改了好久,发现判断中间比两端小的情况判断错了,比两端大的也判断了。。
改掉之后,超时。
最后改成单调栈O(n)判断过了。