BZOJ4756
线段树合并板题
线段树合并就是把两颗线段树合并(这不是废话吗)
线段树维护的信息有可合并性(如最大值,和),因为要update对吧
所以就可以把两颗线段树合并在一起
如果当前位置节点两颗线段树都没有或者只有一颗有,就直接返回这个节点
否则合并一下信息(如求和,取max)然后递归处理左右子树
复杂度最坏一次
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)但一般远远小于这个复杂度
如果用动态开点线段树会大大降低复杂度,基本上可以达到
O
(
l
o
g
n
)
O(logn)
O(logn)一次的级别(线段树是一条链)
Code:
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=100005;
int son[N*20][2],siz[N*20],rt[N];
int vis[N<<1],tot=0,head[N<<1],nxt[N<<1];
int a[N],b[N],n;
int ans[N],sign,cnt=0;
inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
inline void update(int p){siz[p]=siz[son[p][0]]+siz[son[p][1]];}
inline void ins(int &p,int l,int r,int k){
if(!p) p=++cnt;
if(l==r) {siz[p]=1;return;}
int mid=l+r>>1;
if(k<=mid) ins(son[p][0],l,mid,k);
else ins(son[p][1],mid+1,r,k);
update(p);
}
inline int merge(int x,int y,int l,int r){
if(!x || !y) return x+y;
if(l==r) {siz[x]+=siz[y];return x;}
int mid=l+r>>1;
son[x][0]=merge(son[x][0],son[y][0],l,mid);
son[x][1]=merge(son[x][1],son[y][1],mid+1,r);
update(x);
return x;
}
inline int query(int p,int l,int r,int v){
if(l==r) return siz[p];
int mid=l+r>>1;
if(v<=mid) return query(son[p][0],l,mid,v)+siz[son[p][1]];
return query(son[p][1],mid+1,r,v);
}
void dfs(int v){
for(int i=head[v];i;i=nxt[i]){
int y=vis[i];
dfs(y);
rt[v]=merge(rt[v],rt[y],1,sign);
}
ans[v]=query(rt[v],1,sign,a[v])-1;
}
int main(){
n=read();
for(int i=1;i<=n;++i)a[i]=b[i]=read();
sort(b+1,b+n+1),sign=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i)ins(rt[i],1,sign,(a[i]=(lower_bound(b+1,b+sign+1,a[i])-b)));
for(int i=2;i<=n;++i)add(read(),i);
dfs(1);
for(int i=1;i<=n;++i)cout<<ans[i]<<"\n";
return 0;
}