ARC115F Migration

有一棵 \(n\) 个节点的树,编号为 \(i\) 的点权值为 \(h_i\)

你有 \(k\) 个棋子,第 \(i\) 个棋子初始在编号为 \(s_i\) 的点上。你需要进行若干次移动,每次移动可以将一个棋子移到相邻的点上。特别的,多个棋子可以在同一个点上。你需要将第 \(i\) 个棋子移动到编号为 \(t_i\) 的点上。

设第 \(i\) 个棋子在编号为 \(p_i\) 的点上,我们定义当前状态的势能为 \(\sum\limits_{i=1}^{k} h_{p_i}\)。你需要最小化移动过程中势能的最大值。

\(1 \leq n,k \leq 2000\)\(1 \leq h_i \leq 10^9\)

草,学习了一下午发现理解错了。

我们不妨先定义一种严格的偏序关系:若 \(p_u < p_v\)\(p_u = p_v\)\(u < v\),则称 \(u\) 优于 \(v\)

\(dis(u,v)\)\(u\)\(v\) 路径上的最大权值。对于每个点 \(u\),我们考虑对于所有优于 \(u\) 的点 \(v\),求出使得 \(dis(u,v)\) 最小的 \(v\),若有多个取最优的 \(v\)。记 \(f_u=v\),且记 \(w_u=dis(u,v)\)。我们容易在 \(O(n^2)\) 的时间复杂度内求出所有的 \(f_u\)\(w_u\)

我们考虑对于初始状态 \(S\) 和目标状态 \(T\) 进行移动,我们希望每次移动后的势能减少。则每次移动选择某个状态的某个棋子,设其在编号为 \(u\) 的点上,则将其移动到 \(f_u\) 上。假设当前状态的势能为 \(X\),则过程中会产生的最大势能为 \(X-h_u+w_u\),移动后的势能为 \(X-h_u+h_{f_u}\)。我们对于所有不相同的 \(s_i\)\(t_i\) 加入堆,按照 \(w_u-h_u\) 的顺序操作即可。

细节见代码,复杂度 \(O(nk \log k)\)

谁家模拟赛把这个放 T2。

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const long long INF=0x3f3f3f3f3f3f3f3f;
vector<int> G[2010];
long long h[2010],w[2010];
int f[2010];
bool better(int lhs,int rhs){
	return h[lhs]<h[rhs]  ||  h[lhs]==h[rhs]  &&  lhs<rhs;
}
void dfs(int u,int fa,int root,long long pre_max){
	if(better(u,root)){
		if(pre_max<w[root]  ||  pre_max==w[root]  &&  better(u,f[root])){
			w[root]=pre_max;
			f[root]=u;
		}
	}
	for(int i=0;i<G[u].size();i++){
		int v=G[u][i];
		if(v!=fa){
			dfs(v,u,root,max(pre_max,h[v]));
		}
	}
}
struct Node{
	int id;
	long long add_val;
};
bool operator <(const Node &lhs,const Node &rhs){
	return lhs.add_val>rhs.add_val;
}
priority_queue<Node> q1,q2;
long long ans,ans1,ans2;
int s[2010],t[2010],res;
void move1(){
	Node pre=q1.top();
	q1.pop();
	int id=pre.id;
	long long add_val=pre.add_val;
	res-=(s[id]!=t[id]);
	ans=max(ans,ans1+add_val);
	ans1-=h[s[id]];
	ans1+=h[f[s[id]]];
	s[id]=f[s[id]];
	res+=(s[id]!=t[id]);
	if(f[s[id]]){
		q1.push((Node){id,w[s[id]]-h[s[id]]});
	}
}
void move2(){
	Node pre=q2.top();
	q2.pop();
	int id=pre.id;
	long long add_val=pre.add_val;
	res-=(s[id]!=t[id]);
	ans=max(ans,ans2+add_val);
	ans2-=h[t[id]];
	ans2+=h[f[t[id]]];
	t[id]=f[t[id]];
	res+=(s[id]!=t[id]);
	if(f[t[id]]){
		q2.push((Node){id,w[t[id]]-h[t[id]]});
	}
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&h[i]);
	}
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	for(int i=1;i<=n;i++){
		w[i]=INF;
		dfs(i,-1,i,h[i]);
	}
	int k;
	scanf("%d",&k);
	for(int i=1;i<=k;i++){
		scanf("%d %d",&s[i],&t[i]);
		ans1+=h[s[i]];
		ans2+=h[t[i]];
		res+=(s[i]!=t[i]);
		q1.push((Node){i,w[s[i]]-h[s[i]]});
		q2.push((Node){i,w[t[i]]-h[t[i]]});
	}
	ans=max(ans1,ans2);
	while(res){
		if(q1.empty()){
			move2();
		}
		else if(q2.empty()){
			move1();
		}
		else{
			Node pre1=q1.top(),pre2=q2.top();
			if(ans1+pre1.add_val<=ans2+pre2.add_val){
				move1();
			}
			else{
				move2();
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2025-12-21 17:38  Oken喵~  阅读(8)  评论(1)    收藏  举报