題解 簡單的填數
阿新 • • 發佈:2021-07-12
不得不說,真是好題,不管是在題目方面還是在噁心人方面都是
而且我又一次因為打錯字母調了巨久
暴力基本沒法寫,還好puts -1有10pts
考場上我曾試圖將序列分段,但邊界幾乎不會處理
正解極其神仙:
對每個位置構造兩個二元組\(up\)和\(down\),
\(up\)中元素\(val\)表示列舉到位置i時最大可能值,\(cnt\)則是其出現的數量,\(down\)相反
那我們就在每個位置維護了一個可能範圍,可以用來判不合法情況
然後對於每個給出的\(a[i]\),將\(up.val\)和\(down.val\)都置為\(a[i]\),
這時要分情況討論,以\(up\)為例:
- \(up.val<a[i]\)
- \(up.val==a[i]\): 不必操作
- \(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; }