Codeforces Round #525 E - Ehab and a component choosing problem

题目大意:

在一棵树中 选出k个联通块 使得 这k个联通块的点权总和 / k 最大

并且这k个联通块不相互覆盖(即一个点只能属于一个联通块)

如果有多种方案,找到k最大的那种

给定n 有n个点

给定n个点的点权(点权可能出现负数)

给定这个树的n-1条边

当将所有点分成联通块后,比较各个强联通块的点权总和,绝对存在最大值,而点权总和=最大值的也可能有多个

此时 若选择了所有点权总和等于最大值的联通块,那么 /k 之后得到的 ans=这个最大值

  假设继续选择次大值,那么此时 res = (ans*k+次大值 ) /  (k+1) ,显然这个res会因次大值而 res<ans,即选择次大值无法使得答案更优

  所以若我们找到了最大值,最优的选择就是 直接选择所有点权总和等于最大值的联通块,无论点权总和最大值为正负,都是这样

不过由于点权存在负数,若要最大化点权总和,显然不能将点权为负数的点并做联通块

所以当u点要将v点并做联通块时,应先考虑v点的权值是否小于0,若小于0则不并

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=3e5+5;
int n,k,a[N];
LL ans,fe[N];
vector <int> E[N];
void dfs(int u,int fa,int op) {
    fe[u]=(LL)a[u];
    for(int i=0;i<E[u].size();i++) {
        int v=E[u][i];
        if(v==fa) continue;
        dfs(v,u,op);
        fe[u]+=max(fe[v],0LL);
    }
    if(op) ans=max(ans,fe[u]);
    else if(fe[u]==ans) k++, fe[u]=0LL;
    // 搜索最大值个数时 除记录个数外 将fe[u]置零 
    // 防止父亲(或父亲的父亲...)因加上该值又得到最大值 则发生覆盖
}
int main()
{
    while(~scanf("%d",&n)) {
        for(int i=1;i<=n;i++) {
            scanf("%d",&a[i]);
            E[i].clear();
        }
        for(int i=1;i<n;i++) {
            int u,v; scanf("%d%d",&u,&v);
            E[u].push_back(v), E[v].push_back(u);
        }
        ans=-INF; k=0;
        dfs(1,0,1), dfs(1,0,0); 
        // 1时搜出最大值 0时搜出不相互覆盖的最大值的个数
        printf("%I64d %d\n",ans*(LL)k,k);
    }

    return 0;
}
View Code

  

猜你喜欢

转载自www.cnblogs.com/zquzjx/p/10090202.html
今日推荐