[CF1558E]Down Below

122 篇文章 0 订阅

题目

传送门 to CF

思路

时刻维护一个 v i s i t e d    n o d e \rm visited\;node visitednode 的集合。显然我们只需要找到一条增广路经,从 v i s i t e d \rm visited visited 出发,最终走回 v i s i t e d \rm visited visited,然后将路径上的点加入 v i s i t e d \rm visited visited 集合。因为下一步一定可以从任意一个 v i s i t e d \rm visited visited 点出发。

受了 e a s y    v e r s i o n \rm easy\;version easyversion 的影响,总认为可以直接找出最优路径。即,经过的点依次为 ⟨ x 1 , x 2 , … , x k ⟩ \lang x_1,x_2,\dots,x_k\rang x1,x2,,xk,则 max ⁡ i = 1 k ( a x i − ∑ j = 1 i − 1 b x j ) \max_{i=1}^{k}\left(a_{x_i}-\sum_{j=1}^{i-1}b_{x_j}\right) maxi=1k(axij=1i1bxj) 是最小的。

然而这是一张图,变数很多。如果我们正向找,那么面临的问题是 max ⁡ v \max v maxv ∑ b \sum b b 的取舍——目前的 max ⁡ v \max v maxv 较大,但是 ∑ b \sum b b 也更大,就可以让后面出现的瓶颈得到缓释。

如果我们逆向找,总算可以保留 max ⁡ v \max v maxv 最小的了,因为后面的 ∑ b \sum b b 没用。问题随之出现:有可能 v v v 转移出一个比 v v v 更小的值!这样就不能保证复杂度了!一个点可以被更新若干次!

于是还是二分答案。二分之后,似乎必须正向寻找路径,多次走到同一个点,怎么判断 max ⁡ v \max v maxv ∑ b \sum b b 的取舍?图样图森破, t o u r i s t \sf tourist tourist 告诉我们:不用判,发现就赢了!

假设我们有两条路径,均是从 v i s i t e d \rm visited visited 点出发,走到同一个点。两条路径之中,必然有一条路径的 ∑ b \sum b b 更大。我们只需要先走那条路径,然后走另一条路径返回,就是一条合法的增广路径!

正确性是显然的,因为 b b b 恒正。注意增广路径可以自交,因为第一次自交的地方就可以作为路径的终点,而前面的限制条件并没有算错,所以这条路径一定可以走通。

所以只需要 d f s \rm dfs dfs b f s \rm bfs bfs 一次。一个点最多访问一次,复杂度当然是 O ( m ) \mathcal O(m) O(m) 的,最多有 n n n 次增广,加上外层的二分答案的复杂度,总复杂度 O ( n m log ⁡ A ) \mathcal O(nm\log A) O(nmlogA)

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 1005;
const int MaxM = 2005;
struct Edge{
	int to, nxt;
	Edge(){ to = nxt = 0; }
	Edge(int T,int N){
		to = T, nxt = N;
	}
};
Edge e[MaxM<<1];
int head[MaxN], cntEdge;
void addEdge(int a,int b){
	e[cntEdge] = Edge(b,head[a]);
	head[a] = cntEdge ++;
}

const int infty = (1<<30)-1;
int a[MaxN], b[MaxN];
bool vis[MaxN], cur[MaxN];
int pre[MaxN]; // for tracing path
int getPath(int x){
	if(vis[x]) return 0;
	vis[x] = true; // persistent
	return min(infty,getPath(pre[x])+b[x]);
}
int dfs(int now,int x){
	cur[x] = true;
	now = min(now+b[x],infty);
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(e[i].to == pre[x]) continue;
		if(now <= a[e[i].to]) continue;
		if(cur[e[i].to])
			return getPath(x) +
				getPath(e[i].to);
		pre[e[i].to] = x;
		int f = dfs(now,e[i].to);
		if(~f) return f;
	}
	return -1;
}

int n;
bool check(int now){
	memset(vis+1,0,n);
	memset(pre+1,0,n<<2);
	for(vis[1]=1; true; ){
		memcpy(cur+1,vis+1,n);
		bool ok = 1; int f = -1;
		rep(i,1,n) ok = ok && vis[i];
		if(ok) return true; // done
		for(int i=1; i<=n; ++i){
			if(!vis[i]) continue;
			for(int j=head[i]; ~j; j=e[j].nxt)
				if(!vis[e[j].to])
				if(now > a[e[j].to]){
					pre[e[j].to] = i;
					f = dfs(now,e[j].to);
					if(~f){
						now += f; break;
					}
				}
			if(~f) break;
		}
		if(f == -1) return false;
		now = min(now,infty);
	}
	return false; // impossible
}

int main(){
	for(int T=readint(); T; --T){
		n = readint(); // global!
		int m = readint();
		cntEdge = 0; // clear
		memset(head+1,-1,n<<2);
		rep(i,2,n) a[i] = readint();
		rep(i,2,n) b[i] = readint();
		for(int i=1,x,y; i<=m; ++i){
			x = readint(), y = readint();
			addEdge(x,y), addEdge(y,x);
		}
		int L = 0, R = 1e9+1, mid = R>>1;
		for(; L!=R; mid=(L+R)>>1)
			if(check(mid)) R = mid;
			else L = mid+1;
		printf("%d\n",L);
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值