题意:要求构造一个d的排列使得满足d[i/k]<=d[u]且字典序最大。
标程(bzoj上并不能过):
1 #include<bits/stdc++.h> 2 #define mid ((l+r)>>1) 3 using namespace std; 4 const int N=500005; 5 const int eps=1e-7; 6 double k; 7 int tag[N<<2],sum[N<<2],size[N],n,fa[N],ans[N],d[N]; 8 map<int,int> to; 9 bool cmp(int A,int B){return A>B;} 10 void up(int k) {sum[k]=min(sum[k<<1],sum[k<<1|1]);} 11 void down(int k) 12 { 13 if (tag[k]) 14 { 15 tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k]; 16 sum[k<<1]+=tag[k];sum[k<<1|1]+=tag[k]; 17 tag[k]=0; 18 } 19 } 20 void build(int k,int l,int r) 21 { 22 if (l==r) {sum[k]=l;return;} 23 build(k<<1,l,mid);build(k<<1|1,mid+1,r); 24 up(k); 25 } 26 void add(int k,int l,int r,int L,int R,int x) 27 { 28 if (L<=l&&r<=R) {sum[k]+=x;tag[k]+=x;return;} 29 down(k); 30 if (L<=mid) add(k<<1,l,mid,L,R,x); 31 if (R>mid) add(k<<1|1,mid+1,r,L,R,x); 32 up(k); 33 } 34 int qry(int k,int l,int r,int x) 35 { 36 if (l==r) return (x<=sum[k])?l:l+1; 37 down(k);int res; 38 if (x<=sum[k<<1|1]) res=qry(k<<1,l,mid,x); 39 else res=qry(k<<1|1,mid+1,r,x); 40 up(k);//由于前面down标记,查询时也需要up来更新 41 return res; 42 } 43 int main() 44 { 45 scanf("%d%lf",&n,&k); 46 for (int i=1;i<=n;i++) scanf("%d",&d[i]),fa[i]=(int)((double)i/k+eps); 47 for (int i=n;i>=1;i--) size[i]++,size[fa[i]]+=size[i]; 48 build(1,1,n); 49 sort(d+1,d+n+1,cmp); 50 for (int i=n;i>=1;i--) if (d[i]!=d[i+1]) to[d[i]]=i; 51 for (int i=1;i<=n;i++) 52 { 53 if (fa[i]&&fa[i]!=fa[i-1]) add(1,1,n,ans[fa[i]],n,size[fa[i]]-1); 54 int t=qry(1,1,n,size[i]); ans[i]=to[d[t]]--; 55 add(1,1,n,ans[i],n,-size[i]); 56 } 57 for (int i=1;i<=n;i++) printf("%d ",d[ans[i]]); 58 return 0; 59 }
题解:线段树+占位
如果直接贪心:从上到下贪心然后把最小的派给编号最大的是不对的。1 1 1 2 应该是1 1 2 1。
直接按编号从小到大考虑。每一层用size占位,线段树维护从小到大的d序列,每一位表示当前位前面有多少个数没被使用。每次选择最靠前的一个sum[x]>=size[i](单调),如果找到x周围有若干个和sum[x]相等,那么选取最靠后的一个(贪心)。如果当前子树的父亲已经处理完毕,那么还原,继续处理儿子。