1. 程式人生 > 其它 >題解 簡單的填數

題解 簡單的填數

傳送門

不得不說,真是好題,不管是在題目方面還是在噁心人方面都是
而且我又一次因為打錯字母調了巨久

暴力基本沒法寫,還好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;
}