div2贪心+建树

C. Linova and Kingdom

Writing light novels is the most important thing in Linova’s life. Last night, Linova dreamed about a fantastic kingdom. She began to write a light novel for the kingdom as soon as she woke up, and of course, she is the queen of it.

There are n cities and n−1 two-way roads connecting pairs of cities in the kingdom. From any city, you can reach any other city by walking through some roads. The cities are numbered from 1 to n, and the city 1 is the capital of the kingdom. So, the kingdom has a tree structure.

As the queen, Linova plans to choose exactly k cities developing industry, while the other cities will develop tourism. The capital also can be either industrial or tourism city.

A meeting is held in the capital once a year. To attend the meeting, each industry city sends an envoy. All envoys will follow the shortest path from the departure city to the capital (which is unique).

Traveling in tourism cities is pleasant. For each envoy, his happiness is equal to the number of tourism cities on his path.

In order to be a queen loved by people, Linova wants to choose k cities which can maximize the sum of happinesses of all envoys. Can you calculate the maximum sum for her?

Input
The first line contains two integers n and k (2≤n≤2⋅105, 1≤k<n) — the number of cities and industry cities respectively.

Each of the next n−1 lines contains two integers u and v (1≤u,v≤n), denoting there is a road connecting city u and city v.

It is guaranteed that from any city, you can reach any other city by the roads.

Output
Print the only line containing a single integer — the maximum possible sum of happinesses of all envoys.

扫描二维码关注公众号,回复: 11501073 查看本文章

大意:给出n,k和一个点数量为n的树,让其中k个结点变为工业城市,其余为旅游城市。而每个工业城市到根节点1的路径上存在的旅游城市数量之和求最大,并输出最大值。

题目链接:https://codeforces.com/contest/1337/problem/C

解法一:
最大的就是节点一(子儿子为n-1深度为0)
每个旅游城市做出的贡献为:他的子儿子个数减去他的祖先个数(深度个数)
假设它的子儿子都为工厂,则做出的贡献为子儿子个数
因为此时它为旅游区,而之前它的祖先为他做出了贡献,此时它变为了旅游区所以需要减去它祖先的个数
排序,然后有k个工厂则有n-k个旅游城市,选出最大的n-k个旅游城市

#include<bits/stdc++.h>
#define ll long long

using namespace std;

vector<int>G[200001];
ll len[200001];   //高度
int fa[200001];  //父亲节点
ll son[200001]; //子儿子数目
struct lll{
    ll so;
    ll dep;
    int inde;

}ans[200001];
bool cmp(lll &node1,lll &node2){


    return node1.so-node1.dep+1<node2.so-node2.dep+1;  //因为减的是高度所以再+1
}
void dfs(int u,int f,ll step){
    fa[u]=f;
    len[u]=step;
    int n=G[u].size();
    for (int i=0;i<n;i++){
        int v=G[u][i];
        if(v==f) continue;
        dfs(v,u,step+1);
        son[u]+=son[v];
    }

}
int main()
{
    int n,k;
    cin>>n>>k;
    len[0]=0;
    for (int i=0;i<n-1;i++){
        int a,b;
        cin>>a>>b;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    son[1]=G[1].size();
    for (int i=2;i<=n;i++){
        son[i]=G[i].size()-1; //子儿子数等于本身儿子 在进行dfs加上孙子
    }
    dfs(1,0,1);
    for (int i=2;i<=n;i++){
       ans[i].so=son[i];
        ans[i].dep=len[i];
        ans[i].inde=i;
    }
        ll sum=n-1;
    sort(ans+2,ans+n+1,cmp);  
    int i=n;
    k=n-k-1;
    while(k){

        sum+=ans[i].so-len[fa[ans[i].inde]];   //加上每个旅游区的贡献
        k--;
        i--;
    }
    cout<<sum;
}

解法2:
总共有k个工厂,假设全为旅游区,每个工厂做出的贡献为它的深度减去它的子孙的个数,因为如果此节点为工厂那么它的子孙都是工厂(可以自行验证),如果它的子孙不是工厂的话就不是最优解。那么这个工厂做出的贡献为它的深度减去它的子孙个数,因为它的祖先是旅游区,则它得到贡献为它的深度,而之前它的子孙接受了它的贡献,而此时变为了工厂,那么要减去这些贡献。

猜你喜欢

转载自blog.csdn.net/weixin_44711328/article/details/105559231