2021牛客寒假算法基础集训营5 A美丽的路径 二分答案

原题链接:https://ac.nowcoder.com/acm/contest/9985/A

目录

  • 题意
  • 分析
  • Code

题意

有n个点,m条边,每个点有一个全值。起点是s,终点是t,设美丽路径为s到t路程中第k/2+1小的数,求最大的美丽值是多少。

分析

刚开始的想法是找到最大的相邻两点中的较小值,最后被证明错了,赛中也有很多人这样过了,后来数据加强了。

既然不能贪心,只能用二分去取答案。我们把大于等于mid的权值设为1,小于等于mid为0。因为我们要求k/2+1小的数,即路径中第k/2+1小的数是1。

  1. 用并查集维护起点和终点,直接判-1的情况
  2. 如果有连着的两个1,那么最后结果一定是大于mid,因为可以来回一直走这两个1
  3. 去掉上述情况之后要使1的个数大于等于0,起点和终点不能都是0。所以我们第二遍从起点出发沿着101010…这样找下去,如果可以到终点,那么就是可以的

Code

#include 
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();
}



你可能感兴趣的