Codeforces Round #504 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final) (CF1023D)

题目链接:http://codeforces.com/problemset/problem/1023/D

 

题目大意:有一个长度为n的序列a,有q次操作。第i次操作可以用i覆盖一段区间内的数。序列a的每个位置至少被改一次。得到最终的序列,然后将序列里的某些位置变成0,输出一种可能的置零之前的最终序列,或无解

 

思路:通过分析可知,两个相同的数之间的数不可能比端点的数小(除非是零)。这个可以用线段树来维护每个区间的最小值。

还需要考虑0的情况和q的情况。因为q是必须要有的,所以若原来序列没有0,没有q,就是NO。在我们建立线段树前,把0的值置为它的前一个数。这里还要分是否有q,具体看代码。(总之情况较多,建议讨论处理,防止没有考虑全)。。。

代码:(写丑了。。。)

#include<bits/stdc++.h>
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
using namespace std;
const int MAXN=2e5+10;
int Min[MAXN<<2];
int a[MAXN];
int used[MAXN];
int judge[MAXN];
void PushUP(int rt) 
{
       Min[rt] = min(Min[rt<<1] , Min[rt<<1|1]);
}
void build(int l,int r,int rt) {
       Min[rt]=2e9;
	   if (l == r) {
              Min[rt]=a[l];
              return ;
       }
       int m = (l + r) >> 1;
       build(lson);
       build(rson);
       PushUP(rt);
}
void update(int p,int sc,int l,int r,int rt) {
       if (l == r) {
              Min[rt] = sc;
              return ;
       }
       int m = (l + r) >> 1;
       if (p <= m) update(p , sc , lson);
       else update(p , sc , rson);
       PushUP(rt);
}
int query(int L,int R,int l,int r,int rt) {
       if (L <= l && r <= R) {
              return Min[rt];
       }
       int m = (l + r) >> 1;
       int ret = 2e9;
       if (L <= m) ret = min(ret , query(L , R , lson));
       if (R > m) ret = min(ret , query(L , R , rson));
       return ret;
}
int main()
{
	int n,q,f1,f2;
	f1=f2=0;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	{
	    scanf("%d",&a[i]);
	    if(a[i]==0)
	    f1=1;
	    if(a[i]==q)
	    f2=1;
	}
	int tem;
	int flag=1;
	if(f1==0)
	{
	    if(f2==0)
	    flag=0;
	    else
		{
		    build(1,n,1);
		     for(int i=1;i<=n;i++)
		     {
		     	 if(used[a[i]]==0)
		     	 used[a[i]]=i;
		     	 else
		     	 {
		     	     tem=query(used[a[i]],i,1,n,1);
		     	     if(tem<a[i])
		     	     {
		     	     	 flag=0;
						 break;
					  }
		     	     used[a[i]]=i;
				 }
			 }
	    }
		if(flag==0)
		{
			printf("NO\n");
		}
		else
		{
			printf("YES\n");
			printf("%d",a[1]);
			for(int i=2;i<=n;i++)
			printf(" %d",a[i]);
		}
	}
	else
	{
		 if(f2==0)
		 {
		     for(int i=1;i<=n;i++)
		     {
		     	if(a[i]==0)
		     	{
		     		a[i]=q;
		     		break;
				 }
			 }
		 }
		 else
		 {
		 	 if(a[1]==0)
			 a[1]=1;
		 }
			 for(int i=2;i<=n;i++)
			 {
			     if(a[i]==0)
				 a[i]=a[i-1];    	
			 }
			 build(1,n,1);
			 for(int i=1;i<=n;i++)
		     {
		     	 if(used[a[i]]==0)
		     	 used[a[i]]=i;
		     	 else
		     	 {
		     	     tem=query(used[a[i]],i,1,n,1);
		     	     if(tem<a[i])
		     	     {
		     	     	 flag=0;
						 break;
					  }
		     	     used[a[i]]=i;
				 }
			 }
		 if(flag==0)
		{
			printf("NO\n");
		}
		else
		{
			printf("YES\n");
			printf("%d",a[1]);
			for(int i=2;i<=n;i++)
			printf(" %d",a[i]);
		}
	}
	return 0;
}
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值