• [CSP-S模拟测试]:简单的填数(贪心+模拟)


    题目描述

    对于一个长度为$n$,且下标从$1$开始编号的序列$a$,我们定义它是「合法的」,当且仅当它满足以下条件:
    ·$a_1=1$
    ·对于$iin [1,n),a_ileqslant a_{i+1}leqslant a_i+1$且$a_{i+1}$为正整数
    ·对于任意在$a$中出现过的数$v$,记它的出现次数为$s$,则$2leqslant sleqslant 5$
    给定一个长度为$n$的序列$a$,其中有一些位置为$0$,你需要在这些位置上任意填数,使得$a$成为一个合法的序列,并且最大化$a_n$的值。


    输入格式

    第一行一个数$n$,表示序列的长度。
    第二行$n$个整数,第$i$个整数表示$a_i$,如果$a_i=0$,则表示这个位置没有填数。


    输出格式

    如果不存在合法的填数方案,则输出$−1$;否则第一行输出一个整数,表示最大的$a_n$;第二行$n$个正整数,第$i$个数表示完成填数后的序列的第$i$个元素。 如果有多组合法的解,输出任意一组


    样例

    样例输入1:

    7
    0 1 0 0 0 3 0

    样例输出1:

    3
    1 1 2 2 3 3 3

    样例输入2:

    4
    0 0 0 3

    样例输出2:

    -1


    数据范围与提示

    对于$30\%$的数据,$nleqslant 1,000$;
    对于另外$30\%$的数据,数据保证随机生成;
    对于$100\%$的数据,$2leqslant nleqslant 2 imes {10}^5,0leqslant a_ileqslant {10}^5$。


    题解

    对于每个位置维护两个二元组,分别是$up(x,l)$表示当前位置能填的最大值$x$和连续个数$l$;$down(x,l)$表示当前为只能填数的最小值$x$和连续个数$l$。

    求$up$就是尽可能的往上升,求$down$反之。

    第一问就是最后一位的最大值,至于第二问倒着扫一边即可求出。

    时间复杂度:$Theta(n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    int a[200001];
    pair<int,int> up[200001],down[200001];
    int sum[100001],ans[200001];
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	if(a[1]>1){puts("-1");return 0;}
    	a[1]=1;
    	up[1]=down[1]=make_pair(1,1);
    	for(int i=2;i<=n;i++)
    	{
    		up[i]=make_pair(up[i-1].first,up[i-1].second+1);
    		down[i]=make_pair(down[i-1].first,down[i-1].second+1);
    		if(up[i].second>2)
    		{
    			up[i].first++;
    			up[i].second=1;
    		}
    		if(down[i].second>5)
    		{
    			down[i].first++;
    			down[i].second=1;
    		}
    		if(a[i])
    		{
    			if(up[i].first==a[i])up[i].second=min(up[i].second,2);
    			if(up[i].first>a[i])up[i]=make_pair(a[i],2);
    			if(down[i].first<a[i])down[i]=make_pair(a[i],1);
    			if(up[i].first<a[i]||down[i].first>a[i]){puts("-1");return 0;}
    		}
    	}
    	if(up[n].second==1)up[n]=make_pair(up[n-1].first,up[n-1].second+1);
    	printf("%d
    ",up[n].first);
    	ans[n]=up[n].first;
    	sum[a[n]]=1;
    	for(int i=n-1;i;i--)
    	{
    		if(a[i])ans[i]=a[i];
    		else
    		{
    			int flag=min(ans[i+1],up[i].first);
    			if(sum[flag]==5)flag--;
    			ans[i]=flag;
    		}
    		sum[ans[i]]++;
    	}
    	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    Splay 详解
    莫队套值域分块
    浅谈区间众数
    回滚莫队分块
    带修莫队分块
    微服务规划准则
    mysql查询包含逗号的数据,并逗号拆分为多行展现
    python mysql 单连接和连接池简单示例
    代理模式八:装饰者模式
    代理模式七:迭代器模式
  • 原文地址:https://www.cnblogs.com/wzc521/p/11495887.html
Copyright © 2020-2023  润新知