原题链接:https://ac.nowcoder.com/acm/contest/9985/A
题意
有n个点,m条边,每个点有一个全值。起点是s,终点是t,设美丽路径为s到t路程中第k/2+1小的数,求最大的美丽值是多少。
分析
刚开始的想法是找到最大的相邻两点中的较小值,最后被证明错了,赛中也有很多人这样过了,后来数据加强了。
既然不能贪心,只能用二分去取答案。我们把大于等于mid的权值设为1,小于等于mid为0。因为我们要求k/2+1小的数,即路径中第k/2+1小的数是1。
- 用并查集维护起点和终点,直接判-1的情况
- 如果有连着的两个1,那么最后结果一定是大于mid,因为可以来回一直走这两个1
- 去掉上述情况之后要使1的个数大于等于0的个数,即起点和终点不能都是0。所以我们第二遍从起点出发沿着101010…这样找下去,如果可以到终点,那么就是可以的
Code
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
#define fi first
#define se second
#define il inline
#define re register
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const ll INF = 1e18 + 5;
const double eps = 1e-5;
const int MOD = 998244353;
int fa[N], val[N], vis[N], n, m, s, t, flag;
vector<int> g[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void dfs(int x, int mid) {
vis[x] = 1;
for (auto v : g[x]) {
if (val[x] >= mid && val[v] >= mid) flag = 1;
if (!vis[v]) dfs(v, mid);
}
}
void dfs2(int x, int mid) {
vis[x] = 1;
for (auto v : g[x]) {
if (!vis[v] && (val[x] >= mid) != (val[v] >= mid)) dfs2(v, mid);
}
}
bool check(int x) {
flag = 0;
memset(vis, 0, sizeof vis);
dfs(s, x);
if (flag) return true;
if (val[s] < x && val[t] < x) return false;
memset(vis, 0, sizeof vis);
dfs2(s, x);
if (vis[t]) return true;
else return false;
}
void solve() {
int T; cin >> T; while (T--) {
cin >> n >> m >> s >> t;
for (int i = 1; i <= n; i++) cin >> val[i], fa[i] = i, g[i].clear();
for (int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
fa[find(u)] = find(v);
}
if (find(s) != find(t)) {
printf("NO\n");
continue;
}
printf("YES\n");
int l = 1, r = 1e9;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) l = mid + 1;
else r = mid - 1;
}
printf("%d\n", r);
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}