codeforces 1023 D. Array Restoration 并查集

D. Array Restoration
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Initially there was an array $$$a$$$ consisting of $$$n$$$ integers. Positions in it are numbered from $$$1$$$ to $$$n$$$.

Exactly $$$q$$$ queries were performed on the array. During the $$$i$$$-th query some segment $$$(l_i, r_i)$$$ $$$(1 \le l_i \le r_i \le n)$$$ was selected and values of elements on positions from $$$l_i$$$ to $$$r_i$$$ inclusive got changed to $$$i$$$. The order of the queries couldn't be changed and all $$$q$$$ queries were applied. It is also known that every position from $$$1$$$ to $$$n$$$ got covered by at least one segment.

We could have offered you the problem about checking if some given array (consisting of $$$n$$$ integers with values from $$$1$$$ to $$$q$$$) can be obtained by the aforementioned queries. However, we decided that it will come too easy for you.

So the enhancement we introduced to it is the following. Some set of positions (possibly empty) in this array is selected and values of elements on these positions are set to $$$0$$$.

Your task is to check if this array can be obtained by the aforementioned queries. Also if it can be obtained then restore this array.

If there are multiple possible arrays then print any of them.

Input

The first line contains two integers $$$n$$$ and $$$q$$$ ($$$1 \le n, q \le 2 \cdot 10^5$$$) — the number of elements of the array and the number of queries perfomed on it.

The second line contains $$$n$$$ integer numbers $$$a_1, a_2, \dots, a_n$$$ ($$$0 \le a_i \le q$$$) — the resulting array. If element at some position $$$j$$$ is equal to $$$0$$$ then the value of element at this position can be any integer from $$$1$$$ to $$$q$$$.

Output

Print "YES" if the array $$$a$$$ can be obtained by performing $$$q$$$ queries. Segments $$$(l_i, r_i)$$$ $$$(1 \le l_i \le r_i \le n)$$$ are chosen separately for each query. Every position from $$$1$$$ to $$$n$$$ should be covered by at least one segment.

Otherwise print "NO".

If some array can be obtained then print $$$n$$$ integers on the second line — the $$$i$$$-th number should be equal to the $$$i$$$-th element of the resulting array and should have value from $$$1$$$ to $$$q$$$. This array should be obtainable by performing exactly $$$q$$$ queries.

If there are multiple possible arrays then print any of them.

Examples
Input
4 3
1 0 2 3
Output
YES
1 2 2 3
Input
3 10
10 10 10
Output
YES
10 10 10
Input
5 6
6 5 6 2 2
Output
NO
Input
3 5
0 0 0
Output
YES
5 4 2
Note

In the first example you can also replace $$$0$$$ with $$$1$$$ but not with $$$3$$$.

In the second example it doesn't really matter what segments to choose until query $$$10$$$ when the segment is $$$(1, 3)$$$.

The third example showcases the fact that the order of queries can't be changed, you can't firstly set $$$(1, 3)$$$ to $$$6$$$ and after that change $$$(2, 2)$$$ to $$$5$$$. The segment of $$$5$$$ should be applied before segment of $$$6$$$.

There is a lot of correct resulting arrays for the fourth example.

 

题意
长度为n的序列,q次操作,第i次操作必须选择一个区间并把区间全部设为i。已知最后的序列的一部分,未知的位置用0表示。要求判断该序列是否合法,并用任意一种合法的方式在0的位置填充1~q
分析
为了简化表述,用[x]表示序列中一段连续的x。
最后一次操作一定不会被覆盖,第q次操作后只能有一个[q],只有两种合法的情况:
只有一个[q];有若干个[q],且[q]之间必须全部是[0]。
对第q-1次操作的检查也是同理,因为q-1只可能被[q]覆盖,两个[q-1]之间只能出现[0]或[q],只有下面两种合法的情况:
只有一个[q-1];有若干个[q-1],且必须是[q-1][0][q-1]或[q-1][q][q-1]的形式。
为了方便检查两个[q-1]之间是否为[q]或[0],在上一步处理[q]的时候,可以把[q]之间的[0]全部变为[q],最后让q变成连通的一整块,这样就只用检查是否为[q]了。
对第q-2次操作的检查就是:
只有一个[q-2];有若干个[q-2],且两两之间必须全部是[q-2][0][q-2], [q-2][q-1][q-2], [q-2][q][q-2], [q-2][q][q-1][q-2],[q-2][q-1][q][q-1][q-2],...。
注意到合法的情况将越来越多,但其实只需要检查[q-2][x]...[y][q-2]中,与[q-2]直接相邻的两个[x][y]就可以了。如果[x]...[y]中没有[0],那么[x]...[y]全为[q]或[q-1]的充分必要条件,就是[x],[y]都为[q]或[q-1],如果存在反例,那么q-1的检查一定不能通过。 为了方便检查,我们不希望在[x]...[y]有[0]的存在,可以在检查[q-1]的之前,把与[q-1]相邻的[0]全部设为[q-1],在检查[q-2]之前,把[q-2]相邻的[0]全部设为[q-2]。
依次类推,对第i次操作的检查,首先把所有[i]相邻的[0]设为[i],再检查[i][x]..[y][i]的内部,[x]...[y]则一定都是不小于[x], [y]的数,那么合法的充分必要条件就是[x]和[y]都比i大。
这样处理到最后,发现所有的[0]都被填充了,所以直接按[i]整段输出就可以了。
总结
其实按i从1到q的顺序也能完成,最重要的就是,两段[i]之间,不能出现比i小的非0段,只要满足这个性质,再对[0]加以填充,就能得到正确答案了。
代码
#include<stdio.h>

#define N_max 200005
int n,q;
int ipt[N_max],now[N_max];
int lg[N_max],rg[N_max];//并查集的范围
int fst[N_max],nxt[N_max];//并查集的链表

//#define debug there_is_no_bug_at_all_if_you_cannot_see_these_words_:(

int main(){
    scanf("%d %d",&n,&q);
    int last,fr;
    scanf("%d",ipt+1);
    last=ipt[1];fr=1;lg[1]=1;
    for(int i=2;i<=n;++i){
        scanf("%d",ipt+i);
        if(ipt[i]!=last){//出现新的值
            /*将i-1代表的并查集插入链表*/
            nxt[i-1]=fst[last];
            fst[last]=i-1;
            /*在头部记录并查集覆盖的范围,以及值*/
            lg[i-1]=fr;rg[i-1]=i-1;now[i-1]=last;
            //在并查集的尾部也要记录整个并查集范围
            rg[fr]=i-1;
            /*准备下一个并查集*/
            fr=i;last=ipt[i];lg[fr]=fr;
        }
    }
    //把最后一段并查集也添加进来
    nxt[n]=fst[last];
    fst[last]=n;
    lg[n]=fr;rg[n]=n;now[n]=last;
    rg[fr]=n;

    //值为q的并查集至少有一个,如果没有就把一个0设为q
    if(fst[q]==0){
        if(fst[0]==0){//没有0则是不合法的
            printf("NO");return 0;
        }
        now[fst[0]]=q;
        fst[q]=fst[0];
        fst[0]=nxt[fst[0]];nxt[fst[q]]=0;
    }
    int ln;
    for(int t=q;t>=1;--t){//扫描所有值为t的并查集

        if(now[rg[fst[t]+1]]==0)/*因为是从右向左遍历的,如果第一个t右边的并查集是0,则将他设为t*/
            now[rg[fst[t]+1]]=t;
        for(ln=fst[t];ln;ln=nxt[ln])if(now[lg[ln]-1]==0)/*所有t左边的并查集如果是0则设为t*/
            now[lg[ln]-1]=t;
        for(ln=nxt[fst[t]];ln;ln=nxt[ln])/*再次遍历,检查并查集之间是否出现了更小的值*/
            if(now[rg[ln+1]]<t){printf("NO");return 0;}
    }
    printf("YES\n");
    for(int i=1;i<=n;){//输出的时候按并查集输出
        for(int t=i;t<=rg[i];++t)
            printf("%d ",now[rg[i]]);
        i=rg[i]+1;
    }
}
posted @ 2018-08-18 12:09  会打表的toby  阅读(278)  评论(0编辑  收藏  举报