题意:给你一棵数,选出k个联通块,让 k个联通块内所有点权值加和 / k 最大,若结果多个 让k最大,输出权值和 和 k
题解:先找出最大的联通块,然后看看有几个符合的联通块,从下往上选
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
using namespace std;
int n;
long long a[300005];
int vis[300005];
vector<int> e[300005];
vector<int> e1[300005];
long long ans;
struct node
{
long long w;
int id;
};
long long dp[300005];
queue<node>q;
void dfs(int u,int fa)
{
dp[u]=a[u];
int len=e[u].size();
for(int i=0;i<len;i++)
{
int v=e[u][i];
if(v!=fa)
{
dfs(v,u);
if(dp[v]>0)
dp[u]+=dp[v],e1[u].push_back(v);
}
}
if(!q.empty() && q.front().w<dp[u])
{
while(!q.empty())
q.pop();
}
if(q.empty() || q.front().w==dp[u])
{
node tp;tp.id=u;tp.w=dp[u];
q.push(tp);
}
}
int f;
void dfs1(int u,int fa)
{
if(vis[u]==1)
{
f=0;
return;
}
vis[u]=1;
int len=e1[u].size();
for(int i=0;i<len;i++)
{
int v=e1[u][i];
if(v!=fa)
{
dfs1(v,u);
}
}
}
int main() {
while(~scanf("%d",&n))
{
while(!q.empty())
q.pop();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),e[i].clear(),e1[i].clear(),dp[i]=0;
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);
}
dfs(1,-1);
long long ans1=q.front().w,ans2=1;
int cur=q.front().id;
q.pop();
dfs1(cur,-1);
while(!q.empty())
{
f=1;
cur=q.front().id;
q.pop();
dfs1(cur,-1);
if(f==1)
ans2++;
}
printf("%lld %lld\n",ans1*ans2,ans2);
}
return 0;
}