Codeforces Round #525 (Div. 2)E. Ehab and a component choosing problem

E. Ehab and a component choosing problem

题目链接https://codeforces.com/contest/1088/problem/E

题意:

给出一个数,找出k个连通块,使得连通块里面点的和除以k最大。当选择不同数量的连通块有相同的最大值时,要求输出k最大的情况。

题解:

由于这个不等式average(x1,x2,x3...xn)<=max(x1,x2,...xn)成立(当且仅当x1=x2=...xn时,等号成立),而题目所求正好是连通块里面点和的平均值。

我们要让average(x1,x2,x3...xn)最大,一是尽量满足等号成立的条件,二是尽量让连通块里面的权和最大。

由于题目中要求k尽量最大。

所以我们可以进行两次dfs,第一次找出最大的连通块,第二次看看有多少个连通块等于之前找到的最大值,并且更新k的值以及所有点的权值和。

求最大连通块的时候用到最大连续子段和的技巧,负数就舍去,当儿子为正数才更新值。

代码如下:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#define INF 9999999999
using namespace std;

const int N = 3e5+5 ;
typedef long long ll;
int a[N],head[N],vis[N];
int n,tot,k;
struct Edge{
    int u,v,next;
}e[N<<1];
ll dp[N];
ll ans=-INF;
void adde(int u,int v){
    e[++tot].u=u;e[tot].v=v;e[tot].next=head[u];head[u]=tot;
    e[++tot].u=v;e[tot].v=u;e[tot].next=head[v];head[v]=tot;
}
void dfs(int node,int f){
    dp[node]=a[node];
    vis[node]=1;
    for(int i=head[node];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(!vis[v]){
            vis[v]=1;
            dfs(v,f);
            if(dp[v]>0) dp[node]+=dp[v];
        }
    }
    if(!f) ans=max(ans,dp[node]);
    else{
        if(dp[node]==ans){
            k++;
            dp[node]=0;
        }
    }
}
int main(){
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1,u,v;i<=n-1;i++){
        scanf("%d%d",&u,&v);
        adde(u,v);
    }
    dfs(1,0);
    memset(vis,0,sizeof(vis));
    dfs(1,1);
    cout<<ans*k<<" "<<k<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/heyuhhh/p/10079891.html