Codeforces Round #525 (Div. 2) D

题意

从树上选 k k 个连通块,使得 a i k \frac{\sum a_i}{k} 尽可能大,在一样大的基础上,让 k k 尽可能大

题解

我们考虑,显然是倒序排集合,如果说你已经选了 k k 个,要选第 k + 1 k+1
t = S k S + w k + 1 t=\frac{S}{k}-\frac{S+w}{k+1}
比较大小,因为 k w S kw\leq S
t 0 t \geq 0 ,你会发现 k w S kw \leq S ,而这是已知条件,所以只选一个即是最优的。
但是 k k 要越大越好,所以要求选多个最大值集合。

求以 i i 为根节点,必须取该点往下的最大权值(同一集合)
显然有:
f i = m a x ( f j , 0 ) f_i=\sum max(f_j,0)

得到最大值之和,再跑一遍 d p dp ,每次和最大值相同是, k + + k++ ,令当前 d p dp 等于负无穷,然后回溯。即能保证不会重叠。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define sf(x) scanf("%d",&x)
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 405000;

int n,A[maxn];
vector<int>G[maxn];
ll dp[maxn],ans=-inf,k;

void dfs1(int u,int f){
    dp[u]=A[u];
    for(auto v:G[u]){
        if(v==f)continue;
        dfs1(v,u);
        dp[u]+=max(dp[v],1ll*0);
    }
    ans=max(ans,dp[u]);
}

void dfs2(int u,int f){
    dp[u]=A[u];
    for(auto v:G[u]){
        if(v==f)continue;
        dfs2(v,u);
        dp[u]+=max(dp[v],1ll*0);
    }
    if(dp[u]==ans){
        k++;
        dp[u]=-inf;
    }
}

int main(){
    cin>>n;
    FOR(i,1,n)dp[i]=-inf;
    FOR(i,1,n)scanf("%d",&A[i]);
    FOR(i,1,n-1){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(1,-1);
    dfs2(1,-1);
    cout<<ans*k<<" "<<k<<endl;
}

发布了203 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/mxYlulu/article/details/104223173