一棵树每个点有权值(h_i),有(k)个人,初始状态每个人在(s_i),终止状态每个人在(t_i)。
一个状态的代价为(sum h_{a_i}),其中(a_i)表示(i)此时所在位置。
每个时刻选一个人走一步。要求经过的状态中最大值最小。
(n,kle 2000)
如果二分出一个上界(lim),考虑如下做法:让(S)和(T)都走的到可到达的状态中的最低点,如果它们相遇了,那就合法。
把状态以及状态之间的转移看做一个图,显然这是双向的。所以如果规定了唯一的最低点并且它们都能到达,那么它们之间一定能互相到达。
现在对于每个点(x),求(f_x)表示满足(h_y<h_x)(或(h_y=h_x,y<x))的(y)中满足(x)到(y)路径上的最大值最小的(y),(w_x)表示(x o f_x)路径上的最大值。显然((x,f_x))连成了一棵内向森林。
于(S)(或(T))中每次取出(w_x)最小的(x),如果从(x)跳到(f_x)没有超过上限,那就跳过去。由于总和减小,这样操作一定会利于后面的操作,于是一定能到达最低点。
然后发现其实可以不用二分。就是在(S,T)不重合时,取出(S)和(T)中(w_x)最小的(x),扩大上界。
用个堆维护一下,时间(O(nklg n))。
另外还有性质:如果有(f_x=y,f_y=z),则(w_xle w_y)。具体证明可以根据(x,y,z)所在位置分类讨论。所以实际上操作的时候,相当于((x,f_x))连出的树上每次剥叶子。把在同一个叶子上的点一起操作,时间就是(O(nlg n+k))。
using namespace std;
#include <bits/stdc++.h>
#define N 2005
#define ll long long
#define INF 10000000000000
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
int n,K;
int h[N];
struct EDGE{
int to;
EDGE *las;
} e[N*2];
int ne;
EDGE *last[N];
void link(int u,int v){
e[ne]={v,last[u]};
last[u]=e+ne++;
}
int s[N],t[N];
ll mx[N];
int to[N];
ll w[N];
void dfs(int x,int fa,ll s){
mx[x]=max(mx[fa],s);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=fa)
dfs(ei->to,x,s+h[ei->to]-h[x]);
}
priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > qs,qt;
int main(){
// freopen("in.txt","r",stdin);
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&h[i]);
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
link(u,v),link(v,u);
}
scanf("%d",&K);
for (int i=1;i<=K;++i)
scanf("%d%d",&s[i],&t[i]);
mx[0]=-INF;
for (int i=1;i<=n;++i){
dfs(i,0,0);
for (int j=1;j<=n;++j)
if (h[j]<h[i] || h[j]==h[i] && j<i){
if (to[i]==0 || mx[to[i]]>mx[j])
to[i]=j,w[i]=mx[j];
}
}
int cnt=0;
ll S=0,T=0,ans=0;
for (int i=1;i<=K;++i){
S+=h[s[i]],T+=h[t[i]];
if (to[s[i]]) qs.push(mp(w[s[i]],i));
if (to[t[i]]) qt.push(mp(w[t[i]],i));
cnt+=(s[i]!=t[i]);
}
ans=max(S,T);
while (cnt>0/* && (!qs.empty() || !qt.empty())*/){
if (qt.empty() || !qs.empty() && S+qs.top().fi<T+qt.top().fi){
int x=qs.top().se;
qs.pop();
ans=max(ans,S+w[s[x]]);
cnt-=(s[x]!=t[x]);
S-=h[s[x]];
s[x]=to[s[x]];
S+=h[s[x]];
cnt+=(s[x]!=t[x]);
if (to[s[x]])
qs.push(mp(w[s[x]],x));
}
else{
int x=qt.top().se;
qt.pop();
ans=max(ans,T+w[t[x]]);
cnt-=(s[x]!=t[x]);
T-=h[t[x]];
t[x]=to[t[x]];
T+=h[t[x]];
cnt+=(s[x]!=t[x]);
if (to[t[x]])
qt.push(mp(w[t[x]],x));
}
}
printf("%lld
",ans);
return 0;
}