1. 程式人生 > >2018 省選 D1T2 IIIDX

2018 省選 D1T2 IIIDX

LG 取整 find using 字典 define lib roo 自己

題目大意:

給出k、n個數選擇一種字典序最大的排列,使得對於任意的i,di>=d[i/k](下取整 下同)

分析:

很容易想到的是建樹,將i的父親設為[i/k],之後建有向邊。

60分貪心:

將原先的a數組升序排列,直接根據子樹大小分配排位。pai[i]=(同層級剩余的)-(子樹大小)+1; 然而對於di有相同的情況時,也許可能會使得子樹之間值發生交換,仍使得命題成立。

例如: [1,2,2,3] k=2;

正解:[1,2,3,2]

貪心: [1,2,2,3]

問題在於,因為題目中說了滿足單調不降即可,而貪心則極力向後取數,可能會將大的數預留給自己較大編號的後代。如這個例子中,2號取到了3號排位,將4號排位、最大的3留給了4號。而3號就只剩下了2號排位。

我們想最大化較小的編號的值,就讓它盡量往後取排位。 但是由於單調不降,可以讓2號取2號位,4號取3號位,3號取4號位。使得答案更優。

正解:

線段樹。

令原數組a降序排列。 令f[i]表示i號排位左邊還有幾個位置上的數是可以取到的。線段樹維護這個區間內f的最小值。

顯然這個f數組單調不降。對於從小到大的第i個編號,我們每次二分一個x,使得找到一個最靠左的位置,使得f[x]>=size[i],若有多個x,找到最靠右的一個x(這樣使得在x取值最優時,盡可能將大的數留給後面的數。)讓[x~n]區間上的值都減去size[i]

具體實現:

1.先判斷到了i號點時,有沒有進入下一個層級,如果有,將上一個層級預留的值都加回來

(+size[fa]-1),才能二分、利用。

2.二分找到一個p;將p移動到同一個數值的、未用過的最右邊。

3.將p賦值給ans[i],記錄該p已經被用過(詳見代碼中nxt[p]++,這樣保證下次減回來能多減一個位置

4.給子樹預留位置。

詳見代碼:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=500000+10;
int nxt[N],a[N],ans[N],fa[N],size[N];
int n; double k; struct node{ int mi,l,r; int add; #define mix t[x].mi #define lx t[x].l #define rx t[x].r #define lsx x<<1 #define rsx x<<1|1 #define ax t[x].add #define milsx t[lsx].mi #define mirsx t[rsx].mi #define alsx t[x<<1].add #define arsx t[x<<1|1].add }t[N<<2]; void push(int x) { if(!ax) return; milsx+=ax; mirsx+=ax; alsx+=ax; arsx+=ax; ax=0; } void build(int l,int r,int x) { if(l==r) { mix=l,lx=l,rx=r;ax=0;return; } int mid=(l+r)>>1; lx=l,rx=r,ax=0; build(l,mid,lsx); build(mid+1,r,rsx); mix=min(mirsx,milsx); } void add(int l,int r,int x,int c) { if(l<=lx&&rx<=r) { mix+=c; ax+=c;return; } push(x); int mid=(lx+rx)>>1; if(l<=mid) add(l,r,lsx,c); if(mid<r) add(l,r,rsx,c); mix=min(milsx,mirsx); } int find(int x,int d) { if(lx==rx) return lx+(mix<d); push(x); if(mirsx<d) return find(rsx,d); return find(lsx,d); } bool cmp(int a,int b) {return a>b;} void prework() { sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) fa[i]=int(i/k),size[i]=1; for(int i=n-1;i;i--) if(a[i]==a[i+1]) nxt[i]=nxt[i+1]+1; for(int i=n;i;i--) size[fa[i]]+=size[i]; } int main() { scanf("%d%lf",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); prework(); int root=1; build(1,n,root); for(int i=1;i<=n;i++) { if(fa[i]!=fa[i-1]) add(ans[fa[i]],n,root,size[fa[i]]-1); int p=find(root,size[i]); p+=nxt[p];nxt[p]++;p-=nxt[p]-1;ans[i]=p; add(p,n,root,-size[i]); } for(int i=1;i<=n;i++) printf("%d ",a[ans[i]]); return 0; }

2018 省選 D1T2 IIIDX