AT_arc115_f

一个跑了整整 3900ms 的做法。

考虑如果当前有一个状态 AA 能在和不超过 limlim 的前提下到达一个状态 BB,则 BB 在同样条件的前提下也能到达 AA

于是对于初始状态 SS,会发现最优策略一定是先把和变得尽可能小,达到状态 RR,以此让某些要经过很大值的点通过,然后再变成结束状态 TT

如果我们想知道一个状态 AA 在操作过程中和不超过 limlim 的前提下,和尽可能小的状态 BB 是简单的。考虑记录 di,jd_{i,j} 表示一个棋子从 ii 点移动到 jj 点,其他不动,会使和 ss 增加多少。

容易发现我们只会把棋子从 hih_i 大的移动到 hih_i 小的。同时,若 disi,j<disi,kdis_{i,j}<dis_{i,k},先移动到 jj 一定不劣。于是对于每个位置 uu,下一次会移动到的点 touto_u 是固定的,只需要每次找可移动的点移动即可。一共会移动 O(n2)O(n^2) 次,使用优先队列优化后这一部分是 O(n2logn)O(n^2\log n) 的。

但是此时又发现一个问题:求 RR 是否能到达 TT 是困难的。但是由于一开始给出的结论,考虑对于 TT 求出它能到达的和最小的状态 RR',于是只用求 RR 是否能到达 RR'

这个判断是简单的,容易发现,若 RRRR' 中两个对应的棋子的 hh 不同,则一定无解。因为若 RR 中的 hxh_x 小于 RR' 中的 hyh_y,那么说明这个 xx 无法到达 yy,否则 RR 会变化。于是只用判断此时 RR 的和 sumsum 是否有 sum+maxdisx,ylimsum+\max dis_{x,y}\le lim 即可。

同时这个 limlim 显然是可二分的,所以二分答案,复杂度为 O(n2logVlogn)O(n^2\log V\log n)

然而此时大概率还无法通过。于是卡卡常,首先是发现设 S,TS,T 的和分别为 s,ts,t,则二分边界可以设成 l=max(s,t),r=l+109l=\max(s,t),r=l+10^9。同时,发现 at 的 c++17 比 c++20 要快。于是 3900ms 通过。

code:

int n,m,dis[N][N],a[N],b[N],c[N],f[N],g[N],to[N];
priority_queue<pii> q;
int tot,head[N];
struct node{
	int to,nxt;
}e[N<<1];
il void add(int u,int v){
	e[++tot]={v,head[u]},head[u]=tot;
}
void dfs(int u,int f,int s){
	go(i,u){
		int v=e[i].to;
		if(v==f){
			continue;
		}
		dis[s][v]=max(dis[s][u],c[v]-c[s]);
		dfs(v,u,s);
	}
}
bool check(ll lim){
	ll s=0,t=0;
	rep(i,1,m){
		s+=c[a[i]],t+=c[b[i]];
	}
	while(q.size()){
		q.pop();
	}
	rep(i,1,m){
		f[i]=a[i];
		if(to[f[i]]){
			q.emplace(-dis[f[i]][to[f[i]]],i);
		}
	}
	while(q.size()){
		int u=q.top().se,w=-q.top().fi;
		q.pop();
		if(s+w>lim){
			break;
		}
		s+=c[to[f[u]]]-c[f[u]];
		f[u]=to[f[u]];
		if(to[f[u]]){
			q.emplace(-dis[f[u]][to[f[u]]],u);
		}
	}
	while(q.size()){
		q.pop();
	}
	rep(i,1,m){
		g[i]=b[i];
		if(to[g[i]]){
			q.emplace(-dis[g[i]][to[g[i]]],i);
		}
	}
	while(q.size()){
		int u=q.top().se,w=-q.top().fi;
		q.pop();
		if(t+w>lim){
			break;
		}
		t+=c[to[g[u]]]-c[g[u]];
		g[u]=to[g[u]];
		if(to[g[u]]){
			q.emplace(-dis[g[u]][to[g[u]]],u);
		}
	}
	int mx=0;
	rep(i,1,m){
		if(c[f[i]]!=c[g[i]]){
			return 0;
		}
		mx=max(mx,dis[f[i]][g[i]]);
	}
	return s+mx<=lim;
}
void Yorushika(){
	read(n);
	rep(i,1,n){
		read(c[i]);
	}
	rep(i,1,n-1){
		int u,v;read(u,v);
		add(u,v),add(v,u);
	}
	rep(i,1,n){
		dfs(i,0,i);
	}
	rep(i,1,n){
		rep(j,1,n){
			if(c[j]<c[i]){
				if(!to[i]||dis[i][j]<dis[i][to[i]]){
					to[i]=j;
				}
			}
		}
	}
	read(m);
	ll s=0,t=0;
	rep(i,1,m){
		read(a[i],b[i]);
		s+=c[a[i]],t+=c[b[i]];
	}
	ll l=max(s,t),r=l+1e9,ans=1ll*inf*inf;
	while(l<=r){
		ll mid=(l+r)>>1;
		if(check(mid)){
			ans=mid;
			r=mid-1;
		}else{
			l=mid+1;
		}
	}
	printf("%lld\n",ans);
}
signed main(){
	int t=1;
	// read(t);
	while(t--){
		Yorushika();
	}
}

话说好像不用二分,就不卡常了。