神秘代码:难度可能会稍有提升,提前适应多校
C:https://codeforces.ml/problemset/problem/1023/D
题意:给你一个长度为n的数组,然后进行q次操作,对于第i次操作,可以把任意一个区间里的所有数变成i,现在你知道这个数组里面的所有数,但里面可能有一些0,0就是不确定这个位置的数是多少的意思,问你这个数组是否合法,不合法输出NO,合法输出YES和该数组(也许有很多合法的,任意输出一个就好)。
思路:看样列会发现不合法的情况有且只有两种,第一,是两个相等的数之间夹了一个比这个小的数,这样就不合法。第二,就是一个不含零数组里面也不含q(最后一次必然会带来q)
那么对于第一种情况,我们可以用线段树(网上复制的)去维护一个区间里面的最小值,这样就可以很好的降低复杂度了。
至此,代码也就写出来了。
//author CN.TTDragon
#include<bits/stdc++.h>
typedef long long ll;
const ll mod=1e9+7;
const ll maxn=2e5+7;
const double pi=acos(-1);
using namespace std;
int a[maxn],r[maxn]={0};
int seg[maxn<<2];//树
#define lrt rt << 1
#define rrt rt << 1 | 1
void pushup(int rt)
{
seg[rt] = min(seg[lrt], seg[rrt]);
}
void build(int l, int r, int rt)
{
if(l == r)
{
seg[rt] = a[l];
return;
}
int m = (l + r) >> 1;
build(l,m,lrt);//左
build(m+1,r,rrt);//右
pushup(rt);//这个就是表示父节点是儿子节点的最小值
}
int query(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R) //这里的意思是,收缩到这个区间里面的时候
{
return seg[rt];
}
int m = (l + r) >> 1;
int ret = 0x7f7f7f7f;
if(m >= L)
ret = min(ret, query(L, R, l, m, lrt));
if(m < R)
ret = min(ret, query(L, R, m+1, r, rrt));
return ret;
}
int main()
{
ios::sync_with_stdio(false);
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
r[a[i]]=i;//维护每个数最后一次出现
}
if(r[q]==0)//最大的q没有出现过
{
if(r[0]==0)//同时还没有0
{
cout<<"NO"<<endl;
return 0;
}
else
{
a[r[0]]=q;
r[q]=r[0];
}
}
//把0 填了,这两个循环必然能搞定所有的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];
}
}
//接下来就是处理不合法的过程了
bool ok = 1;
build(1, n, 1);//建树去 ,看懂了
for(int i = 1; i <= n; i++)
{
int tmp;
if(!r[a[i]]) //这什么意思 ,先忽略
continue;
//这个数出现过
tmp = query(i, r[a[i]], 1, n, 1);//查询最小值
if(tmp < a[i]) //比当前值小,不合法
{
ok = 0;
break;
}
}
if(ok)
{
cout<<"YES"<<endl;
for(int i = 1; i <= n; i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
else
{
cout<<"NO"<<endl;
}
return 0;
}
写在最后:感觉明天就会因为太菜被gg们踢出群了。

京公网安备 11010502036488号