题解 简单的填数

传送门

不得不说,真是好题,不管是在题目方面还是在恶心人方面都是
而且我又一次因为打错字母调了巨久

暴力基本没法写,还好puts -1有10pts
考场上我曾试图将序列分段,但边界几乎不会处理
正解极其神仙:
对每个位置构造两个二元组\(up\)\(down\)
\(up\)中元素\(val\)表示枚举到位置i时最大可能值,\(cnt\)则是其出现的数量,\(down\)相反
那我们就在每个位置维护了一个可能范围,可以用来判不合法情况
然后对于每个给出的\(a[i]\),将\(up.val\)\(down.val\)都置为\(a[i]\)
这时要分情况讨论,以\(up\)为例:

  1. \(up.val<a[i]\): 不合法,puts -1
  2. \(up.val==a[i]\): 不必操作
  3. \(up.val>a[i]\)\(up.val=a[i]\)但为了维护up始终是最大值的性质,应使up.cnt=2,因为\(up.val\)已经大于\(a[i]\)了,所以\(cnt=2\)一定是合法的,且能使\(up\)更趋于变大

于是扫一遍可以出\(a_n\)最大值
至于反推序列,真的极其毒瘤
我尝试利用\(up\)\(down\)图像交点的性质,将序列分段处理,但分段的边界总是出问题
正解是真的神仙:
这里是维护了一个\(up\)推出的\(max\{a_n\}\),可以构造一个up的逆操作反推回去

for (int i=1; i<=n; ++i) ++cnt[a[i]];
for (int i=n; i; --i) {
	if (a[i]) ans[i]=a[i];
	else {
		if (cnt[ans[i+1]]==5) ans[i]=ans[i+1]-1;
		else ans[i]=min(up[i].val, ans[i+1]);
		++cnt[ans[i]];
	}
}

每个位置的\(up\)值尽可能大,但无法保证合法
\(a[n]\)一定合法,且一定对应着至少一个可行方案
画出图像的话,\(up\)的线在\(a[n]\)上面,而我们反推的序列是一条从\(a[n]\)反向延伸的曲线
所以我们要尽量抬高构造的曲线,即在保证合法的情况下尽量取大,直到两条线相交

是真的神仙构造题

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int a[N], b[N], cnt[N], ans[N];
struct ele{int val, cnt;}up[N], down[N];

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	n=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	if (a[1]&&a[1]!=1) {puts("-1"); return 0;}
	a[1]=1;
	up[1].val=down[1].val=1;
	up[1].cnt=down[1].cnt=1;
	for (int i=2; i<=n; ++i) {
		//cout<<i<<": "<<up[i-1].val<<' '<<up[i-1].cnt<<' '<<down[i-1].val<<' '<<down[i-1].cnt<<endl;
		if (up[i-1].cnt==2) up[i].val=up[i-1].val+1, up[i].cnt=1;
		else up[i].val=up[i-1].val, up[i].cnt=up[i-1].cnt+1;
		if (down[i-1].cnt==5) down[i].val=down[i-1].val+1, down[i].cnt=1;
		else down[i].val=down[i-1].val, down[i].cnt=down[i-1].cnt+1;
		//cout<<i<<": "<<up[i].val<<' '<<up[i].cnt<<' '<<down[i].val<<' '<<down[i].cnt<<endl;
		//cout<<"a["<<i<<"]: "<<a[i]<<endl;
		if (a[i]) {
			if (a[i]<down[i].val || a[i]>up[i].val) {puts("-1"); return 0;}
			
			if (up[i].val>a[i]) {
				up[i].val=a[i]; up[i].cnt=2;
			}
			
			if (down[i].val<a[i]) {
				down[i].val=a[i]; down[i].cnt=1;
			}
		}
		//if (a[i]||i==n) {lst[i]=lst2; cnt[i]=cnt2; cnt2=0; lst2=a[i];}
	}
	//if (a[n] && (a[n]<down[n].val||a[n]>up[n].val)) {puts("-1"); return 0;}
	
	a[n]=up[n].cnt==1?up[n].val-1:up[n].val;
	for (int i=1; i<=n; ++i) ++cnt[a[i]];
	for (int i=n; i; --i) {
		if (a[i]) ans[i]=a[i];
		else {
			if (cnt[ans[i+1]]==5) ans[i]=ans[i+1]-1;
			else ans[i]=min(up[i].val, ans[i+1]);
			++cnt[ans[i]];
		}
	}
	
	printf("%d\n", up[n].cnt==1?up[n].val-1:up[n].val);
	for (int i=1; i<=n; ++i) printf("%d ", ans[i]);
	printf("\n");

	return 0;
}
posted @ 2021-07-12 21:45  Administrator-09  阅读(10)  评论(0编辑  收藏  举报