pldzy
2022-07-19 21:25:17
DSY 传送门:Migration
二分 + 贪心 + 双向搜索(思想)。
看到“最大值最小”,考虑二分答案如何做。对于每个状态权值的上限
为了方便计算,不妨设定当
考虑如何实现函数 check(mid)
。即考虑若限定在移动过程中权值的最大值,如何判定能否从
不难由此想到贪心:
对于每个棋子初始所在点
记
然后对于每个棋子,设其所在点为
然后我们就能得到起始和终止状态分别能达到的最优状态了。然后本题这个方法的关键点在于:操作具有可逆性。所以我们可以双向搜索。即,当且仅当起始和终止状态所能达到的权值最小的状态相同,它们两个才能互达。
然后此题就结束了。(写代码的时候也没觉得这么复杂啊...
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
const int maxn = 2005;
int n, k, s[maxn], t[maxn], w[maxn];
int dis[maxn], son[maxn], ts[maxn], tt[maxn], bst[maxn];
int l, r = 2000ll * 1e9, ans, u, v;
int cnt, hd[maxn];
struct node{
int to, nxt;
}e[maxn << 2];
inline int rd(){
int s = 0, x = 1; char ch = getchar();
while(ch < '0' or ch > '9'){if(ch == '-') x = -1; ch = getchar();}
while(ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return x * s;
}
inline void add(int u, int v){
e[++cnt] = (node){v, hd[u]}; hd[u] = cnt;
}
inline void dfs(int u, int fa, int rt){
dis[u] = max(dis[fa], w[u]);
if(w[u] < w[rt] or (w[u] == w[rt] and u < rt))
if(dis[son[rt]] > dis[u] or !son[rt]) son[rt] = u;
for(int i = hd[u]; i; i = e[i].nxt){
int v = e[i].to; if(v == fa) continue;
dfs(v, u, rt);
}
}
inline void pre(int x){
memset(dis, 0, sizeof dis);
dfs(x, 0, x), bst[x] = dis[son[x]];
}
inline bool chck(int v){
rep(i, 1, k) ts[i] = s[i], tt[i] = t[i];
int ss, st; ss = st = 0;
rep(i, 1, k) ss += w[s[i]], st += w[t[i]];
if(max(ss, st) > v) return 0;
int flg = 1;
while(flg){
flg = 0;
rep(i, 1, k) if(son[ts[i]] and ss - w[ts[i]] + bst[ts[i]] <= v)
ss += - w[ts[i]] + w[son[ts[i]]], ts[i] = son[ts[i]], flg = 1;
rep(i, 1, k) if(son[tt[i]] and st - w[tt[i]] + bst[tt[i]] <= v)
st += - w[tt[i]] + w[son[tt[i]]], tt[i] = son[tt[i]], flg = 1;
}
rep(i, 1, k) if(ts[i] != tt[i]) return 0;
return 1;
}
signed main(){
n = rd();
rep(i, 1, n) w[i] = rd();
rep(i, 2, n)
u = rd(), v = rd(), add(u, v), add(v, u);
k = rd();
rep(i, 1, k) s[i] = rd(), t[i] = rd();
rep(i, 1, n) pre(i);
while(l <= r){
int mid = l + r >> 1;
if(chck(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
} printf("%lld\n", ans);
return 0;
}