Codeforces Round 635 (Div. 1) A. Linova and Kingdom

题目链接:Problem - 1336A - Codeforcest

题意:给定一棵树,选择k个点,求每个点到根(1是根节点)之间最短路径上非选择点数量之和的最大值。

Codeforces Round 635 (Div. 1) A. Linova and Kingdom_第1张图片

对于任意一个父节点,我们如果选择父节点而不选择他的子节点,那么一定不如选择他的子节点,可以得到更大的数。

根据我们得出的结论,来计算每个节点对答案的贡献。对于一棵树,我们先考虑每个节点对他的贡献。首先,这个节点对答案的正贡献是到根节点的距离deep,因为我们选择一个节点就一定会选择他的子节点,所以选择这个点会让他所有的子孙节点的贡献-1,所以负贡献为s[i](s数组表示该节点子孙节点的数量),总的贡献就是 deep - s[i] ,将每个点的贡献排序,从大到小选择k个,即为最大值。

#include 
#define int long long

using namespace std;
const int N = 200010 , M = N * 2 ;
int n , m ;
int h[N] , e[M] , ne[M] , idx ;
int s[N] ;
vector res ;

void add(int a , int b)
{
    e[idx] = b , ne[idx] = h[a] , h[a] = idx ++ ;
}
void dfs(int u , int fa , int deep)
{
    int c = 0 ;
    for(int i = h[u] ; ~i ; i = ne[i])
    {
        int j = e[i] ;
        if(j == fa) continue ;
        c ++ ;
        dfs(j , u , deep + 1) ;
        s[u] += s[j] ;
    }
    s[u] += c ;
    res.push_back(deep - s[u]) ;
}
signed main()
{
    cin >> n >> m ;
    memset(h , -1 , sizeof h) ;
    for(int i = 1 ; i <= n - 1 ; i ++)
    {
        int a , b ;
        scanf("%lld%lld",&a,&b) ;
        add(a , b) , add(b , a) ;
    }
    dfs(1 , -1 , 0) ;
    int sum = 0 ;
    sort(res.begin() , res.end()) ;
    reverse(res.begin() , res.end()) ;
    for(int i = 0 ; i < m ; i ++)
        sum += res[i] ;
    cout << sum << endl ;
    return 0 ;
}

你可能感兴趣的:(算法,xcpc,算法,c++,数据结构)