1. 程式人生 > >Codeforces Round #525 E - Ehab and a component choosing problem

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