神秘代码:难度可能会稍有提升,提前适应多校
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们踢出群了。