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;
}
posted @ 2021-03-23 11:33  jz_597  阅读(150)  评论(0编辑  收藏  举报