【Usaco2017 Jan】Promotion Counting(树上差分+树状数组/线段树合并)

传送门

%%%ldxldx神仙线段树合并

本来想练练线段树合并的,但是看了看题就树上差分秒切了

考虑到我们维护一个权值为下标的树状数组

每次dfsdfs一个点就updateupdate这个点

每到一个点先记录一下目前比他大的点有多少个

dfsdfs完他的儿子后再查询一下现在比他大的点有多少个

相减就是答案了

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
	return res*f;
}
const int N=100005;
int tr[N],a[N],b[N],num,adj[N],ans[N],n,cnt,nxt[N<<1],to[N<<1];
inline void addedge(int u,int v){
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
inline void init(){
	sort(b+1,b+n+1);
	num=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+num+1,a[i])-b;
}
inline int lowbit(int x){
	return (x&(-x));
}
inline void update(int pos){
	for(;pos<=n;pos+=lowbit(pos))tr[pos]+=1;
}
inline int query(int pos,int res=0){
	for(;pos;pos-=lowbit(pos))res+=tr[pos];
	return res;
}
void dfs(int u,int fa){
	update(a[u]);
	int pre=query(num)-query(a[u]);
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa)continue;
		dfs(v,u);	
	}
	int now=query(num)-query(a[u]);
	ans[u]=now-pre;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=b[i]=read();
	init();
	for(int i=2;i<=n;i++){
		int f=read();addedge(f,i),addedge(i,f);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)cout<<ans[i]<<'
';
}
原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366353.html