給出 k 和 n 個數,構造一個序列使得 d[i]>=d[i/k] ,而且字典序最大。c++
據說,當年省選的時候,這道題擋住了大批的高手,看上去十分簡單,實際上那道彎段時間內是轉不過來的。ide
首先,一個套路是,將這個序列的關係抽象成一棵樹,i的父親是floor(i/k),咱們要要求子樹內部的點的權值都比父親大。ui
咱們觀察子任務的特殊限制,di不同?spa
咱們想,把原序列從大到小排序,在樹上dfs給點賦值,在給一個點賦值時,要在序列上預留出siz[這棵子樹]的位置,用來給子樹內部的點賦值(排序就是爲了方便在序列上預留位置)。code
可是,若是有重複的元素就辦不了了,好比n=4, k=2 序列是1,1,1,2.
blog
排好應該是1 1 2 1,可是咱們上面的方法會排成1 1 1 2
排序
爲何呢?遞歸
咱們從大到小排序,應該獲得2 1 1 1.咱們遞歸,首先是樹根節點,須要預留4個位置,因此確定要選最後一個(1),而後遞歸到第一個子樹(序號爲2),size爲2,須要在這個子樹留兩個位置,因此咱們把第二個1給了序號2這個位置,而後那個2就給了它的兒子(序號4)可是實際上,咱們能夠給它留一個1,把這個4給位置3.
it
我這麼分析會很亂,可是咱們於情於理考慮一下,咱們解決完了位置2,若想字典序最大,咱們應該先最大化3這個位置。event
因此咱們應該在給序號2找到最大值以後,應該爲他的子樹預留一些值,使這些值在合法的基礎上儘量小。
因此咱們應該排序後,創建一個序列c,c[i]的值表明排好序的序列上,i及i左邊的全部值還剩下多少個能夠選的。
(序列c一開始確定是1,2,3,4,5……)
而後咱們每次爲一個點賦值,應該找到最靠左的一個位置i,使全部j>=i知足c[j]>=siz[這個點的子樹]。
而且選完以後,要找到這個位置往右最右邊和他值相等的那個位置(前提是沒有被用過的)。
而後,用區間減法計算貢獻,以後接着處理這個點的兄弟節點,沒有了兄弟節點以後在進入某個點的子節點。
進入一個子節點時,要將以前預留的位置(區間減去的數值)加回來,可是已經分配出去的(父親的)權值就不要加回來了。
因此是siz-1
代碼:
1 #include<bits/stdc++.h> 2 #define db double 3 using namespace std; 4 const int N=500005,inf=1e9; 5 struct segtree{ 6 int l,r,ls,rs,mn,lz; 7 }t[N*4];int a[N],b[N],ans[N],m,rt; 8 db k;int n,siz[N],fa[N],cnt[N],o=0; 9 bool cmp(int u,int v){return u>v;} 10 void pushup(int cur){ 11 int ls=t[cur].ls,rs=t[cur].rs; 12 t[cur].l=t[ls].l;t[cur].r=t[rs].r; 13 t[cur].mn=min(t[ls].mn,t[rs].mn); 14 } void pushdown(int cur){ 15 int ls=t[cur].ls,rs=t[cur].rs,d=t[cur].lz; 16 t[ls].mn+=d;t[ls].lz+=d; 17 t[rs].mn+=d;t[rs].lz+=d; 18 t[cur].lz=0;return ; 19 } void build(int x,int l,int r){ 20 if(l==r){ 21 t[x].l=t[x].r=l;t[x].ls=t[x].rs=-1; 22 t[x].mn=l;return ; 23 } int mid=l+r>>1; 24 t[x].ls=o++;t[x].rs=o++; 25 build(t[x].ls,l,mid);build(t[x].rs,mid+1,r); 26 pushup(x);return ; 27 } void update(int x,int l,int r,int c){ 28 if(l<=t[x].l&&t[x].r<=r) 29 {t[x].mn+=c;t[x].lz+=c;return ;} 30 pushdown(x);int mid=t[x].l+t[x].r>>1; 31 if(l<=mid) update(t[x].ls,l,r,c); 32 if(mid<r) update(t[x].rs,l,r,c); 33 pushup(x);return ; 34 } int query(int x,int p){ 35 if(t[x].l==t[x].r) 36 return t[x].mn>=p?t[x].l:t[x].l+1; 37 pushdown(x);int mid=t[x].l+t[x].r>>1; 38 if(p<=t[t[x].rs].mn) return query(t[x].ls,p); 39 else return query(t[x].rs,p);return 0; 40 } int main(){ 41 scanf("%d%lf",&n,&k);rt=o++; 42 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 43 sort(a+1,a+1+n,cmp);build(rt,1,n); 44 for(int i=n-1;i;i--) 45 if(a[i]==a[i+1]) cnt[i]=cnt[i+1]+1; 46 else cnt[i]=0; 47 for(int i=1;i<=n;i++) 48 fa[i]=(int)floor(i/k),siz[i]=1; 49 for(int i=n;i;i--) siz[fa[i]]+=siz[i]; 50 for(int i=1;i<=n;i++){ 51 if(fa[i]&&fa[i]!=fa[i-1]) 52 update(rt,ans[fa[i]],n,siz[fa[i]]-1); 53 int x=query(rt,siz[i]);ans[i]=x; 54 x+=cnt[x];cnt[x]++;x-=(cnt[x]-1); 55 update(rt,x,n,-siz[i]); 56 } for(int i=1;i<=n;i++) 57 printf("%d ",a[ans[i]]); 58 return 0; 59 }