ARC115F - Migration
一棵树每个点有权值\(h_i\),有\(k\)个人,初始状态每个人在\(s_i\),终止状态每个人在\(t_i\)。
一个状态的代价为\(\sum h_{a_i}\),其中\(a_i\)表示\(i\)此时所在位置。
每个时刻选一个人走一步。要求经过的状态中最大值最小。
\(n,k\le 2000\)
如果二分出一个上界\(lim\),考虑如下做法:让\(S\)和\(T\)都走的到可到达的状态中的最低点,如果它们相遇了,那就合法。
把状态以及状态之间的转移看做一个图,显然这是双向的。所以如果规定了唯一的最低点并且它们都能到达,那么它们之间一定能互相到达。
现在对于每个点\(x\),求\(f_x\)表示满足\(h_y<h_x\)(或\(h_y=h_x,y<x\))的\(y\)中满足\(x\)到\(y\)路径上的最大值最小的\(y\),\(w_x\)表示\(x\to f_x\)路径上的最大值。显然\((x,f_x)\)连成了一棵内向森林。
于\(S\)(或\(T\))中每次取出\(w_x\)最小的\(x\),如果从\(x\)跳到\(f_x\)没有超过上限,那就跳过去。由于总和减小,这样操作一定会利于后面的操作,于是一定能到达最低点。
然后发现其实可以不用二分。就是在\(S,T\)不重合时,取出\(S\)和\(T\)中\(w_x\)最小的\(x\),扩大上界。
用个堆维护一下,时间\(O(nk\lg n)\)。
另外还有性质:如果有\(f_x=y,f_y=z\),则\(w_x\le w_y\)。具体证明可以根据\(x,y,z\)所在位置分类讨论。所以实际上操作的时候,相当于\((x,f_x)\)连出的树上每次剥叶子。把在同一个叶子上的点一起操作,时间就是\(O(n\lg 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\n",ans);
return 0;
}