您的位置:首页 > 其它

J. 青出于蓝胜于蓝(dfs序+树状数组)

2018-03-29 15:09 162 查看
武当派一共有 nnn 人,门派内 nnn 人按照武功高低进行排名,武功最高的人排名第 111,次高的人排名第 222,... 武功最低的人排名第 nnn。现在我们用武功的排名来给每个人标号,除了祖师爷,每个人都有一个师父,每个人可能有多个徒弟。
我们知道,武当派人才辈出,连祖师爷的武功都只能排行到 ppp。也就是说徒弟的武功是可能超过师父的,所谓的青出于蓝胜于蓝。
请你帮忙计算每个人的所有子弟(包括徒弟的徒弟,徒弟的徒弟的徒弟....)中,有多少人的武功超过了他自己。

输入格式

输入第一行两个整数 n,p(1≤n≤100000,1≤p≤n)n, p(1 \le n \le 100000, 1 \le p \le n)n,p(1≤n≤100000,1≤p≤n)。
接下来 n−1n-1n−1 行,每行输入两个整数 u,v(1≤u,v≤n)u, v(1 \le u, v \le n)u,v(1≤u,v≤n),表示 uuu 和 vvv 之间存在师徒关系。

输出格式

输出一行 nnn 个整数,第 iii 个整数表示武功排行为 iii 的人的子弟有多少人超过了他。
行末不要输出多余的空格

样例输入

10 5
5 3
5 8
3 4
3 1
2 1
6 7
8 7
9 8
8 10

样例输出

0 0 2 0 4 0 1 2 0 0
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(x) (x&(-x))
typedef long long ll;
using namespace std;
/*
树状数组就是来求前缀和,有忘了,wuwuwu...
我知道要求dfs序,但是不知道怎么来求一段区间内的小于等于某个值的数量
我们都是从最小值网上走的,我们是按照val,来的,所以直接查询,在【l,r】
内的数量就可以了,优美
*/
const int maxn=1e5+10;
int cnt;
int head[maxn*2];
struct Edge{
int v,nt;
}edge[maxn*2];
void add_edge(int u,int v){
edge[cnt].nt=head[u];
edge[cnt].v=v;
head[u]=cnt++;
}
int l[maxn],r[maxn];

void dfs(int now,int fa,int& id){
l[now]=++id;//
for(int i=head[now];i!=-1;i=edge[i].nt){
int v=edge[i].v;
if(v==fa) continue;
dfs(v,now,id);
}
r[now]=id;
}

void print(int n){
for(int i=1;i<=n;i++){
printf("i:%d %d %d\n",i,l[i],r[i]);
}
}
int n;
int tr[maxn];
int get_sum(int x){
int sum=0;
while(x>0){
sum+=tr[x];
x-=lowbit(x);
}
return sum;
}

void update(int x,int val){
while(x<=n){
tr[x]+=val;
x+=lowbit(x);
}
}
int ans[maxn];

int main(){
cnt=0;
memset(head,-1,sizeof(head));
int rt;
scanf("%d %d",&n,&rt);
for(int i=1;i<n;i++){
int u,v;
scanf("%d %d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
int id=0;
dfs(rt,-1,id);
//	print(n);
for(int i=1;i<=n;i++){
ans[i]=get_sum(r[i])-get_sum(l[i]);
update(l[i],1);
}
for(int i=1;i<=n;i++){
if(i==1) printf("%d",ans[i]);
else	printf(" %d",ans[i]);
}
printf("\n");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: