Codeforces Round #525 E - Ehab and a component choosing problem
阿新 • • 發佈:2018-12-09
題目大意:
在一棵樹中 選出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]; vectorView Code<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; }