题意:
给定一棵大小为 n n n树,走过每条边需要花费 1 1 1时间,安装软件又需要花费 c i c_i ci时间,需要遍历整棵树并回到起点,想让所有点中到达时间+安装时间的最大值最小,问这个值是多少
范围&性质: 1 ≤ n ≤ 5 × 1 0 5 , 1 ≤ c i ≤ 1 0 9 1\le n\le 5\times10^5,1\le c_i\le 10^9 1≤n≤5×105,1≤ci≤109
分析:
我们对于节点 u u u的儿子,按照最优方案下安装时间排序,我们二分一个最优时间,对于这些点我们从大往小的加入队列中,若当前的点无法直接插入到队列末尾,那就将他插入到最后一个元素的前面,这样能保证他影响到的点最少,要注意最后将 1 1 1点得到的方案,和 c 1 + ( n − 1 ) ∗ 2 c_1+(n-1)*2 c1+(n−1)∗2取一次 m a x max max
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
const int maxn = 5e5+5;
const long long inf = 1e15+5;
long long f[maxn],head[maxn],siz[maxn],c[maxn];
int n,cnt=0,tot;
struct edge
{
int to,nxt;
}e[maxn<<1];
struct node
{
int f,t;
bool operator<(const node &b)const
{
return f>b.f;
}
}p[maxn];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool check(long long x)
{
int i,ed=0;
long long last=0;
for(int i=1;i<=tot;i++)
{
if(last+p[i].f<=x) last+=p[i].t,ed=i;
else if(last-p[ed].t+p[i].t+p[ed].f>x) return false;
else last+=p[i].t;
}
return true;
}
void dfs(int u,int fa)
{
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
}
tot=0;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
p[++tot].f=f[v];
p[tot].t=siz[v]*2ll;
}
sort(p+1,p+tot+1);
long long l=p[1].f,r=inf,mid;
while(l<r)
{
mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
if(fa) f[u]=c[u];
else f[u]=c[u]+((n-1)<<1);
if(tot&&f[u]<l+1) f[u]=l+1;
}
void work()
{
int a,b;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b);add(b,a);
}
dfs(1,0);
printf("%lld\n",f[1]);
}
}
int main()
{
zzc::work();
return 0;
}