题目翻译:
zxyer来到了一个神奇的公司工作,之所以神奇,是因为这个公司的员工的职位并不与他们的水平相称,有的职位极低的职员的经验非常丰富,而有些经理甚至老板都是个萌新。有一天,zxyer收到了老板打来的电话,要求他安排一下让所有员工包括老板本人都去向下层员工学习一下工作经验。可是机智的zxyer很快就明白一件事情,显然不能安排一个员工到他的上司那儿学习经验,这样会太尴尬,同样就更不可能到上司的上司那儿去啦!所以只能安排一个员工到比它职位低的,且属于他管的员工那儿去学习经验。
并且,一个员工能够学习到经验当且仅当他要去学习的那位员工的经验值比他高,那么zxyer就把这个问题丢给了你,要求你求出每个员工能够向几位员工学习经验?
注:在本题中编号为1的员工为老板,他是没有上司的。且上司关系保证是一棵树。
输入:
1个整数n,表示共有n个员工,接下来第i+1行,每行一个整数,表示标号为i的员工的经验值,再接下来n-1行,每行一个整数。表示第2~n个员工的上司
输出:
一共n行,表示n个员工每个人可以学习到经验的人数
样例输入:
5
804289384
846930887
681692778
714636916
957747794
1
1
2
3
样例输出:
2
0
1
0
0
数据范围:
对于50%的数据 n<=2500
对于100%的数据 n<=100000 经验值ai<= 1000000000
————————————————我是分割线————————————————
这道题目稍难,需要巧解。
显然平衡树可以暴力过此题(多一个log,就是跑的慢)
然后我们讲讲巧解、
假如我们用权值线段树来求解这一道题,那么我们在dfs遍历整棵树的时候,在一个点放进权值线段树之前,先将答案减去目前在权值线段树中比该点权值大的数的个数,然后遍历这个点的子树,最后再在答案中加上目前在权值线段树中比该点权值大的数的个数,就是答案啦
当然,主席树也可以做这题,不过还是没标程跑的快QAQ
由于数据范围很大,所以需要离散。
下面贴代码
#include<cstdio> #include<algorithm> #define MN 100005 #define M 231072 #define ls (k<<1) #define rs (k<<1|1) using namespace std; int n,j=0,tot; int head[MN],s[MN],num[MN],ans[MN]; int t[M<<1]; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } struct edge{ int to,next; }g[MN]; void ins(int u,int v){g[++tot].next=head[u];head[u]=tot;g[tot].to=v;} void update(int k,int add){ t[k+=M]+=add; for(k>>=1;k;k>>=1)t[k]=t[ls]+t[rs]; } int query(int l){ int sum=0; for(l+=M-1;l!=1;l>>=1) { if(~l&1)sum+=t[l+1]; } return sum; } void dfs(int x){ update(num[x],1); ans[x]=-query(num[x]); for(int i=head[x];i;i=g[i].next)dfs(g[i].to); ans[x]+=query(num[x]); } int main(){ freopen("boss.in","r",stdin); freopen("boss.out","w",stdout); n=read(); for(int i=1;i<=n;i++)num[i]=s[i]=read(); sort(s+1,s+n+1); for(int i=1;i<=n;i++) if(s[i]!=s[i-1])s[++j]=s[i]; for(int i=1;i<=n;i++)num[i]=lower_bound(s+1,s+j+1,num[i])-s; int x; for(int i=1;i<n;i++)ins(read(),i+1); dfs(1); for(int i=1;i<=n;i++)printf("%d ",ans[i]); fclose(stdin); fclose(stdout); }