题目大意:
把数放入一棵树中,要求父亲节点权值小于等于儿子。问最大的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;
}