【題解】 P4364 [九省聯考 2018] IIIDX
阿新 • • 發佈:2022-04-21
首先把題目轉化成給一棵樹賦權值,讓他成為一個小根堆,字典序最大。
考慮貪心,對於當前點 \(u\) 的子樹,選擇當前最大的 \(siz_u\) 個權值,然後遞迴求解。這樣在 \(d_i\) 互不相同的時候容易發現是正確的。
如果 \(d_i\) 中存在相同的,我們發現當在決策 \(u\) 的子樹時,子樹內可以選擇和 \(u\) 相等的權值,把大的權值讓給和 \(u\) 的同深度的其他點(顯然點的編號越小深度越小)。
這個時候我們就不能遞迴每個子樹了,正確的做法是按照點的編號來決策。
先將 \(d\) 從小到大排序。
假設當前點是 \(u\) ,我們找到最大的可能權值在排好序的陣列中的最小位置,讓 \(u\)
可是這樣我們發現 \(i\) 的值不是真實的 \(f\) ,同時因為我們無法確定預留了哪些權值,所以真實的 \(f\) 沒辦法直接維護。
這個時候我們讓之前所有預留的值都儘量靠前選(跳過 \(i\) ),這是為了讓當前的決策點儘量靠後,此時的 \(f_i\) 就是維護的陣列的字首最小值,這個模擬一下這個過程就能得出。
我們發現 \(f_i\) 具有單調性,所以決策每個點的時候線上段樹上二分就可以了。
注意在決策點 \(u\) 時要把父親為他預留的位置空出來。
時間複雜度 \(O(n\log n)\)
\(\sf{Code}\)
#include<bits/stdc++.h> #define N 2001001 #define MAX 2001 using namespace std; typedef long long ll; typedef double db; const ll inf=1e18; inline void read(ll &ret) { ret=0;char c=getchar();bool pd=false; while(!isdigit(c)){pd|=c=='-';c=getchar();} while(isdigit(c)){ret=(ret<<1)+(ret<<3)+(c&15);c=getchar();} ret=pd?-ret:ret; return; } ll n,d[N],pos[N],siz[N],a[N]; db k; struct node { ll minn,tag,ls; }seg[N]; inline node operator +(node x,node y) { return node{min(x.minn,y.minn),0ll,x.ls}; } inline void add(ll pos,ll num) { seg[pos].minn+=num; seg[pos].ls+=num; seg[pos].tag+=num; return; } inline void pushdown(ll pos) { add(pos<<1,seg[pos].tag); add(pos<<1|1,seg[pos].tag); seg[pos].tag=0; return; } inline void build(ll pos,ll l,ll r) { if(l==r) seg[pos].minn=seg[pos].ls=n-l+1; else { ll mid=l+r>>1; build(pos<<1,l,mid); build(pos<<1|1,mid+1,r); seg[pos]=seg[pos<<1]+seg[pos<<1|1]; } return; } inline void upgrade(ll pos,ll l,ll r,ll s,ll t,ll num) { if(l>=s&&r<=t) return add(pos,num); else if(l>t||r<s) return; pushdown(pos); ll mid=l+r>>1; upgrade(pos<<1,l,mid,s,t,num); upgrade(pos<<1|1,mid+1,r,s,t,num); seg[pos]=seg[pos<<1]+seg[pos<<1|1]; return; } inline ll query(ll pos,ll l,ll r,ll siz,ll d) { if(l==r) return l; ll mid=l+r>>1; pushdown(pos); if(siz<=min(seg[pos<<1].minn,min(d,seg[pos<<1|1].ls))) return query(pos<<1|1,mid+1,r,siz,min(d,seg[pos<<1].minn)); return query(pos<<1,l,mid,siz,d); } bool vis[N]; map<ll,ll>mp; signed main() { read(n); cin>>k; for(int i=1;i<=n;i++) read(a[i]); sort(a+1,a+n+1); for(int i=1;i<=n;i++) { if(!mp[a[i]]) mp[a[i]]=i; } build(1,1,n); for(int i=1;i<=n;i++) siz[i]=1; for(int i=n;i;i--) siz[ll(floor(i/k))]+=siz[i]; for(int i=1;i<=n;i++) { ll fa=ll(floor(i/k)); if(fa) upgrade(1,1,n,1,pos[fa],siz[i]); ll tmp=a[query(1,1,n,siz[i],inf)]; pos[i]=mp[tmp]; mp[tmp]++; upgrade(1,1,n,1,pos[i],-siz[i]); } for(int i=1;i<=n;i++) printf("%lld ",a[pos[i]]); exit(0); }