题目大意:
把数放入一棵树中,要求父亲节点权值小于等于儿子。问最大的BFS序。
题解:
显然要让前面的尽可能大,考虑当前这个数最大能放多少,这个节点要为他的子树预留下子树大小个点。
然后建一颗权值线段树,维护区间最小后缀和。这个数最大能放多少就是最大的数使得后缀和的前缀最小值>=他子树大小。
每访问一个节点将他父亲给他预留的东西清空,更新答案后为他的子树预留下子树大小个点。
对于一个节点的多个儿子只清空一次。
代码:
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; int ans,cnt,n,last[1000005],tree[5000005],tag[5000005],a[1000005],b[1000005],c[1000005],sz[1000005],vis[1000005],f[1000005]; double k; struct node{ int to,next; }e[1000005]; void add(int a,int b){ e[++cnt].to=b; e[cnt].next=last[a]; last[a]=cnt; } void push_down(int x){ if (tag[x]){ tag[x<<1]+=tag[x]; tag[x<<1|1]+=tag[x]; tree[x<<1]+=tag[x]; tree[x<<1|1]+=tag[x]; tag[x]=0; } } void change(int now,int l,int r,int x,int y,int val){ if (l>y || r<x) return; if (l>=x && r<=y){ tree[now]+=val; tag[now]+=val; return; } push_down(now); int mid=(l+r)>>1; change(now<<1,l,mid,x,y,val); change(now<<1|1,mid+1,r,x,y,val); tree[now]=min(tree[now<<1],tree[now<<1|1]); } void query(int now,int l,int r,int key){ if (tree[now]>=key){ ans=r; return; } if (l==r) return; push_down(now); int mid=(l+r)>>1; if (tree[now<<1]>=key){ ans=mid; query(now<<1|1,mid+1,r,key); } else query(now<<1,l,mid,key); } void dfs(int x){ sz[x]=1; for (int i=last[x]; i; i=e[i].next){ int V=e[i].to; dfs(V); sz[x]+=sz[V]; } } int main(){ scanf("%d%lf",&n,&k); for (int i=1; i<=n; i++) add((int)floor((double)i/k),i); dfs(0); for (int i=1; i<=n; i++){ scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+n+1); int cnt=unique(b+1,b+n+1)-b-1; for (int i=1; i<=n; i++) { int id=lower_bound(b+1,b+cnt+1,a[i])-b; f[id]=a[i]; a[i]=id; change(1,1,n,1,a[i],1); } vis[0]=1; for (int i=1; i<=n; i++){ ans=0; if (!vis[(int)floor((double)i/k)]) { change(1,1,n,1,c[(int)floor((double)i/k)],sz[(int)floor((double)i/k)]-1); vis[(int)floor((double)i/k)]=1; } query(1,1,n,sz[i]); c[i]=ans; change(1,1,n,1,c[i],-sz[i]); } for (int i=1; i<=n; i++) printf("%d ",f[c[i]]); return 0; }