CF1558E Down Below

一张无向图 $G$,点有点权 $a_i,b_i$,初始在 $1$,有 $val$ 的权值,需要遍历所有节点。

可以从 $u$ 移动到 $v$ 当且仅当 $(u,v) \in E, val > a_v$,不能回头。第一次移动到 $u$ 会使得 $val \gets val + b_u$。问 $val$ 至少为多少。

$1 \le n \le 10^3,1 \le m \le 2 \times 10^3$,所有点度数 $\ge 2$。

二分答案转化为判定性问题。

若没有不能回头走的限制,那么可以维护所有已经经过的点,选择 $a$ 最小的邻点扩展。

加上限制后我们仍维护已经遍历到的节点,记节点集合为 $S$,此时力量值为 $初值+\sum_{i \in S} b_i$,考虑怎样进行扩展。

  • 存在合法路径 $x \to p_1 \to p_2 \to … \to p_k \to y$ 满足 $x,y \in S,p_i \notin S$。

  • 存在合法路径 $x \to p_1 \to p_2 \to … \to p_1 \to x$ 满足 $x \in S,p_i \notin S$,即一段重复路径和环。

对于第一种路径,dfs 查找即可。对于第二种路径,发现其可以被拆为两条同起点的合法路径 $x \to p_1 \to … $,$x \to p’_1 \to p’_2 \to …$,且同起点的两条合法路径一定可以合成第二种路径。每次扩展一条即可,另一条会在之后被扩展。

时间复杂度 $O(nm\log V)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<bits/stdc++.h>
using namespace std;

#define int long long
#define pb push_back
#define U(x) ((int)x.size())
const int N=1e3+100,INF=1e18;
#define gc getchar()
#define rd read()
inline int read(){
int x=0,f=0; char c=gc;
for(;c<'0'||c>'9';c=gc) f|=(c=='-');
for(;c>='0'&&c<='9';c=gc) x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}

int n,m,a[N],b[N];
vector<int> G[N],vc;

int pre[N],vis[N],cur;
void dfs(int u,int fu,int w){
if(cur) return; if(pre[u]) return cur=u,void(); pre[u]=fu;
for(int v:G[u]){
if(a[v]>=w||v==fu) continue;
if(vis[v]) return cur=u,void();
dfs(v,u,w+b[v]);
}
}

int chk(int mid){
for(int i=1;i<=n;++i) pre[i]=vis[i]=0;
vis[1]=1,vc={1};
while(U(vc)<n){
cur=0; for(int u:vc) for(int v:G[u]) if(a[v]<mid&&!vis[v]) dfs(v,u,mid+b[v]);
if(!cur) break; for(;!vis[cur];cur=pre[cur]) vc.pb(cur),mid+=b[cur],vis[cur]=1;
for(int i=1;i<=n;++i) pre[i]=0;
}
return U(vc)==n;
}

void solve(){
n=rd,m=rd;
for(int i=2;i<=n;++i) a[i]=rd;
for(int i=2;i<=n;++i) b[i]=rd;
for(int i=1,x,y;i<=m;++i) G[x=rd].pb(y=rd),G[y].pb(x);

int l=0,r=INF;
while(l<r){
int mid=l+r>>1;
if(chk(mid)) r=mid;
else l=mid+1;
}
printf("%lld\n", l);

for(int i=1;i<=n;++i) G[i]={};
}

signed main(){

int T=rd;
while(T--) solve();

return 0;
}