zoukankan      html  css  js  c++  java
  • Codeforces Round #504:D. Array Restoration

    D. Array Restoration

    题目链接:https://codeforces.com/contest/1023/problem/D

    题意:

    给出一个序列,现在要求对一个全为0的序列执行q次操作,每次操作都要选定一段区间然后将区间上面的值变为i(i为操作的次数)。最终使得0序列变为之前给出的序列。

    原序列中如果存在0,那么说明这个值是任意的。

    最后要求输出经过q次操作之后的序列。

    题解:

    我们首先可以想到不可行的情况:在原序列中若存在一个数a,假设其出现次数大于1,那么两端为a的中间区间部分,是没有值比a小的。

    还有一种情况也不可行,就是原序列中没有0且最大值小于q。

    当把这两种情况排除过后,就是可行的情况了,之后输出方案就好了。

    输出方案的时候还要解决0的问题,我的解决方案是让0等于其右边或左边的数,我是将一段连续的0缩点后进行处理的。

    代码如下:

    #include <bits/stdc++.h>
    #define pii pair<int,int>
    #define INF 99999999
    using namespace std;
    
    const int N = 2e5+5;
    int n,q;
    int a[N],cnt[N];
    struct node{
        int l,r;
    }z[N],All[N];
    int f[N][20];
    vector <int> vec[N];
    int main(){
        cin>>n>>q;
        int num = 0,mx=0;
        a[0]=-1;a[n+1]=-1;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            mx=max(mx,a[i]);
            if(a[i]==0&&a[i-1]!=0){
                num++;
            }
            if(a[i]==0){
                cnt[i]=num;
                if(a[i-1]!=0) z[num].l=i-1;
            }else if(a[i-1]==0) z[num].r=i;
            vec[a[i]].push_back(i);
        }
        if(a[n]==0) z[num].r=n+1;
        int flag = 0;
        for(int i=1;i<=n;i++){
            if(a[i]==0 || vec[a[i]].size()<=1) continue ;
            int len = vec[a[i]].size()-1;
            All[a[i]].l=vec[a[i]][0];
            All[a[i]].r=vec[a[i]][len];
        }
        for(int i=1;i<=n;i++) for(int j=1;j<=18;j++) f[i][j]=INF;
        for(int i=1;i<=n;i++) f[i][0]=(a[i]==0 ? INF : a[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=16;j++)
                f[i][j]=min(f[i][j-1],f[i+(1ll<<(j-1))][j-1]);
        for(int i=1;i<=n;i++){
            if(vec[a[i]].size()<=1) continue ;
            int l = All[a[i]].l,r = All[a[i]].r;
            l++;r--;
            int K = log((r-l+1));
            int mn = min(f[l][K],f[r-(1<<K)+1][K]);
            if(mn<a[i]){
                flag=1;
                break ;
            }
        }
        int ok=0;
        if(q>mx) ok=1;
        if(ok) for(int i=1;i<=n;i++){
            if(a[i]==0){
                a[i]=q;
                ok=2;
                break ;
            }
        }
        if(flag||ok==1) cout<<"NO";
        else{
            cout<<"YES"<<endl;
            for(int i=1;i<=n;i++){
                if(a[i]!=0){
                    cout<<a[i]<<" ";
                }
                else{
                    int l = z[cnt[i]].l,r = z[cnt[i]].r;
                    if(l<1 && r>n) cout<<q<<" ";
                    else if(l<1) cout<<a[r]<<" ";
                    else cout<<a[l]<<" ";
                }
            }
        }
        return 0;
    }
    View Code

    之后看了下标程,用的是并查集orz...

    标程的想法就比较巧妙了,首先把之前说的第二种不可行情况判断一下,然后开始骚操作。

    其基本思想为:值从大到小进行区间修改的操作,对于当前位置的数而言,用并查集来维护i之后小于或等于它的第一个位置

    因为我们是从大到小开始涂色,对于第一次涂色,肯定能找到第一个小于或等于它的位置;对于第i次涂色,假设当前位置为pos,如果pos+1的值比pos的值大,那么它之前已经操作过了,说明已经用并查集处理过了,那么f(pos+1)就是比pos+1的值小的第一个位置;如果此时找到的值还比pos的值大,那么就继续寻找;如果比pos的值小,那么就不可行。

    注意上面查找值时要满足值的存在区间范围,并且为了输出方案,我们还要对区间进行修改,这时只用对0进行修改就好了~比它大的都不用管。

    具体代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N =2e5+10;
    int n,q;
    int f[N],l[N],r[N];
    int a[N];
    
    int find(int x){
        return f[x]==x ? f[x] : f[x]=find(f[x]);
    }
    
    int main(){
        cin>>n>>q;
        for(int i=0;i<=q;i++) l[i]=n+1,r[i]=0;
        for(int i=1;i<=n+1;i++) f[i]=i;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            l[a[i]]=min(l[a[i]],i);
            r[a[i]]=max(r[a[i]],i);
        }
        if(l[q]>r[q]){
            if(l[0]>r[0]){
                cout<<"NO";
                return 0;
            }
            a[l[0]]=q;
            f[l[0]]=l[0]+1;
        }
        for(int i=q;i>=0;i--){  
            for(int j=find(l[i]);j<=r[i];j=find(j)){
                if(a[j] && a[j]<i){
                    cout<<"NO";
                    return 0;
                }
                a[j]=i;
                f[j]=find(j+1);
            }
        }
        puts("YES");
        for(int i=1;i<=n;i++) printf("%d ",a[i]?a[i]:1);
        return 0;
    }
    View Code
  • 相关阅读:
    1004: 画图
    1002: 数字排序问题
    1003: 相邻数对问题
    1001: 图像旋转问题
    1000: 数塔
    springday05-go1
    springday04-go2
    springday04-go1
    springday03-go2
    Android—PopupWindow的简单使用
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10181378.html
Copyright © 2011-2022 走看看