题目大意:给你一个数组,数组是经过q次区间覆盖后的结果,第i次覆盖是把区间内的值赋值为i,其中有若干个地方数值未知(就是0),让你判断这个数组是否可以经过覆盖后得到的,如果可以,输出任意一种可行数组。
思路:不合法的情况只有2种。1:两个相同的数字中间出现了比它小的数字,比如: 6 5 6 就不合法,因为覆盖6的时候是覆盖连续的一段区间,而5比6先覆盖,所以这种情况不存在。我赛后看AC代码的时候发现有的人只是判断是否出现谷形的情况,这种是不对的。
比如这种样例:3 3 3 1 2 这种判断方法会输出NO,但实际上却可以覆盖。那么怎么找任意区间内的最小值呢?线段树啊(比赛结束前10分钟才想到。。。)。
2:最大值没出现过而且没有0,因为最大值是最后一次覆盖的,所以肯定有最大值。
那么我们就可以写代码了:
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<string> #include<map> #include<set> #include<bitset> #include<vector> #include<cmath> #include<queue> #include<stack> #define INF 0x3f3f3f3f #define LL long long #define mk(a,b) make_pair(a,b) #define pii pair<int,int> using namespace std; const int maxn=200010; map<int,int> mp; int a[maxn],mi[maxn]; bool vis[maxn]; int minv[4*maxn]; int ql,qr; int query(int o,int l,int r){//查询最小值 int mid=l+(r-l)/2,ans=INF; if(ql<=l&&r<=qr)return minv[o]; if(ql<=mid)ans=min(ans,query(o*2,l,mid)); if(mid<qr)ans=min(ans,query(o*2+1,mid+1,r)); return ans; } void build(int o,int l,int r){ if(l==r){ minv[o]=a[l]; return; } int mid=l+(r-l)/2; build(o*2,l,mid); build(o*2+1,mid+1,r); minv[o]=min(minv[o*2],minv[o*2+1]); } int main(){ int n,m,mi=INF,mx=0,cnt=0; bool flag=1; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); mx=max(mx,a[i]); if(a[i]==0){//为了方便建线段树,把0赋值为INF a[i]=INF; cnt++; } } if(mx<m&&cnt==0){//第二种情况 printf("NO\n"); return 0; } build(1,1,n);//建树 for(int i=1;i<=n;i++){ if(a[i]==INF){ continue; } if(vis[a[i]]){//如果之前出现过 ql=mp[a[i]],qr=i; if(query(1,1,n)<a[i]){//如果小于a[i]值 flag=0; break; } } vis[a[i]]=1; mp[a[i]]=i; } if(flag==0){//情况1 printf("NO\n"); return 0; } for(int i=1,j;i<=n;i++){//从前往后扫一遍 if(a[i]!=INF)continue; else{ if(mx<m)a[i]=m;//如果最大值没出现,就赋值为最大值 else a[i]=a[i-1];//否则赋值为他前面的值 mx=max(mx,a[i]); } } for(int i=n,j;i>=1;i--){ if(a[i]!=INF&&a[i]!=0)continue;//防止a[1]为0的情况 else{ if(mx<m)a[i]=m; else a[i]=a[i+1]; mx=max(mx,a[i]); } } printf("YES\n"); for(int i=1;i<=n;i++) printf("%d ",a[i]); }
原文地址:https://www.cnblogs.com/pkgunboat/p/9498169.html
时间: 2024-10-29 18:00:27