zoukankan      html  css  js  c++  java
  • 计蒜客 青出于蓝胜于蓝(dfs序+树状数组)

    题目描述

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

    输入格式

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

    输出格式

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

    样例输入


    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

    分析

    ①将每条边输入进去,然后dfs求出这棵树的dfs序,在求dfs序的过程中,数组 num2 记录每个节点的孩子节点数量。

    正如样例输入,dfs序为 5,3,4,1,2,8,7,6,9,10

    ②遍历我们找到的dfs序,数组 d 记录每个编号在dfs序中的位置,为了统一处理,我的dfs序列是从1开始计数的,而不是0。

    这一步是为了将这个树形结构序列化,方便我们进行求和操作

    ③遍历数组d,num数组记录武功排行为 i 的人的子弟有多少人超过了他。

               a.   num [ i ] = getsum( d [ i ] + num2[ i ] ) – getsum ( d [ i ] )

    由于我们我们是从小到大枚举 i ,因此 getsum( d [ i ] + num[ i ] ) – getsum ( d [ i ] ) 就是满足条件的子弟数量。

               b.   change( d[ i ], 1)

    观察第三步中的a步骤,我们发现,num2[i]在求出num[i]后再也没用过,因此num2 与 num 可以共用一个储存空间。

    代码:

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stdlib.h>
    #include <string.h>
    #define MAX 100000
    using namespace std;
    vector<int> edge[MAX];
    int n, root;
    int cnt = 1;//记录个数,用于构建dfs序 
    bool vis[MAX];//记录每条边是否访问过,用于构建dfs序 
    int dfs_seq[MAX];//记录dfs序 
    int c[MAX];//树状数组 
    int num[MAX];//记录每个编号有多少个满足要求的徒弟 
    int d[MAX];//记录每个节点在dfs序中的位置 
    void add(int a, int b){
    	edge[b].push_back(a);
    	edge[a].push_back(b);
    }
    
    //树状数组操作 
    int lowbit(int x){
    	return x & (-x);
    }
    
    void change(int x, int v){
    	while(x <= n){
    		c[x] += v;
    		x += lowbit(x);
    	}
    }
    
    int getsum(int x){
    	int sum = 0;
    	while(x > 0){
    		sum += c[x];
    		x -= lowbit(x);
    	}
    	return sum;
    }
    //构建dfs序 
    int dfs(int root){
    	int sum = 0;
    	vis[root] = 1;
    	dfs_seq[cnt++] = root;
    	//cout << dfs_seq[cnt - 1] << " ";
    	for(int i = 0; i < edge[root].size(); i++){
    		if(!vis[edge[root][i]])
    			sum += dfs(edge[root][i]) + 1;
    	}
    	num[root] = sum;
    	return sum;
    }
    
    int main() {
    	scanf("%d %d", &n, &root);
    	for(int i = 1; i < n; i++){
    		int a, b;
    		scanf("%d %d", &a, &b);
    		//将此边加入 
    		add(a, b);
    	}
    
    	dfs(root);
    
    	//记录每个节点在dfs序中的位置 
    	for(int i = 1; i <= n; i++){
    		d[dfs_seq[i]] = i;
    	}
    
    	for(int i = 1; i <= n; i++){
    		num[i] = getsum(d[i] + num[i]) - getsum(d[i]);
    		change(d[i], 1);
    	}
    
    	//依次输出 
    	for(int i = 1; i <= n; i++){
    		cout << num[i] << "";
    	}
        return 0;
    }
  • 相关阅读:
    ubuntu10官方镜像安装硬盘自动分区失败的问题
    ubuntu10的pci扩展卡驱动安装失败后检查方法
    day7集合
    day6字符编码
    day5 dict
    day4 list,tuple
    day2 int,bool,str
    day1
    函数一
    注册登录
  • 原文地址:https://www.cnblogs.com/woxiaosade/p/10892219.html
Copyright © 2011-2022 走看看