Codeforces Round #504 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final)-D. Array Restoration

D. Array Restoration

http://codeforces.com/contest/1023/problem/D

题意:给你一个有n个整数的数组,q次操作,第i次操作选择一个区间(不能为空)把这个区间里面的数都变成i(i>.=1&&i<=q),现在把这个变化过的数组中的一些数字变成0,问你能不能得到变成0之间的序列。如果序列可以恢复输出yes并且打印出这个序列,否则输出no

思路:通过分析我们可以判断序列无法恢复的情况,因为大的数字可以覆盖晓得数字,我们可以找到每个数字出现的区间,如果在这个区间里面有比他更小的数字,那么这个数字是无法恢复的,输出no。下面我们就开始恢复这个序列,我们从q开始恢复,如果q在变化后的序列总如果没有出现过,我们就要找到一个0,并用q代替这个0的位置,如果序列中没有0,那么这个序列是无法恢复的,因为每次选的区间不能为空,并且q是最后进行的操作,没有其他数字可以覆盖他,下面我们从小到大判断判断每个数字出现的区间里面有没有比他更小的数字出现,如果有就输出no,如果区间里面有0就把这个区间范围内的0变成当前询问的数字。我们可以使用线段树进行区间查询最小值

#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>

using namespace std;
const int maxn=2e5+50;
int a[maxn];
int Left[maxn];
int Right[maxn];
int minn[maxn*4];
void PushUp(int rt)
{
    minn[rt]=min(minn[rt*2],minn[rt*2+1]);
}
void Build(int l,int r,int rt)
{
    if(l==r)
    {
        minn[rt]=a[l];
        return ;
    }
    int mid=(l+r)/2;
    Build(l,mid,rt*2);
    Build(mid+1,r,rt*2+1);
    PushUp(rt);
}
void update(int x,int l,int r,int rt)
{
    if(l==r)
    {
        minn[rt]=x;
        a[l]=x;
        return ;
    }
    int mid=(l+r)/2;
    if(minn[rt*2]==0)
    {
        update(x,l,mid,rt*2);
    }
    if(minn[rt*2+1]==0)
    {
        update(x,mid+1,r,rt*2+1);
    }
    PushUp(rt);
}
int query(int l,int r,int L,int R,int rt,int x)
{
    if(l<=L&&r>=R)
    {
        if(minn[rt]==0)
        {
            update(x,L,R,rt);
        }
        return minn[rt];
    }
    int mid=(L+R)/2;
    int ans=1e9;
    if(mid>=l) ans=min(ans,query(l,r,L,mid,rt*2,x));
    if(mid<r) ans=min(ans,query(l,r,mid+1,R,rt*2+1,x));
    PushUp(rt);
    return ans;
}
int main()
{
    int n,q;
    cin>>n>>q;
    memset(minn,0,sizeof(minn));
    for(int i=0; i<=q; i++)
    {
        Left[i]=n+1;
        Right[i]=0;
    }
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        Left[a[i]]=min(Left[a[i]],i);
        Right[a[i]]=max(Right[a[i]],i);
    }

    if(Left[q]>Right[q])
    {
        if(Left[0]!=n+1)
        {
            Left[q]=Right[q]=Left[0];
            a[Left[0]]=q;
        }
        else
        {
            printf("NO\n");
            return 0;
        }
    }
    int flag=0;
    Build(1,n,1);
    for(int i=q; i>=1; i--)
    {
        if(Left[i]>Right[i]) continue;
        int temp=query(Left[i],Right[i],1,n,1,i);
        if(temp<i)
        {
            flag=1;
            break;
        }
    }
    if(flag)
    {
        printf("NO\n");
        return 0;
    }
    printf("YES\n");
    for(int i=1; i<=n; i++)
    {
        if(i!=1) printf(" ");
        if(a[i]!=0)
        {
            printf("%d",a[i]);
        }
        else printf("1");
    }
    printf("\n");
    return 0;
}
/*
50 2
0 1 0 1 0 0 1 0 1 1 0 1 1 1 2 2 0 2 0 2 0 2 0 0 2 2 2 0 0 0 0 1 0 1 0 0 1 0 1 0 0 1 1 0 1 1 0 1 0 0
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值