zoukankan      html  css  js  c++  java
  • Codeforces 1307D Cow and Fields

    Description

    给定一个有 $n$ 个节点 $m$ 条边的有向图,一个顶点集 $S$。

    你需要选择两个顶点 $u,v$($u e v,uin S,vin S$)并连接这两个顶点(允许 $u,v$ 之间已经有连边),求连接后从顶点 $1$ 到顶点 $n$ 最短路的最大值。

    注意,该操作仅能进行一次。

    保证给定的图联通。

    Solution

    我们可以预处理出 $1 o u$ 的最短路记为 $dis_{1 u}$,$u o n$ 的最短路记为 $dis_{2 u}$。

    这个可以通过正反各 BFS 一遍得到。

    接下来介绍两种不同的解法。

    Solution1

    对于 $u$,必须经过它 的最短路长度显然是 $dis_{1 u} + dis_{2 u}$。

    现在,我们在 $u, v$ 之间添加了一条边。

    那么,设想 $dis_{1 u}$ 的求解过程,它是可以从任何一个和 $u$ 相邻的点转移过来的,这些转移点中显然不包含 $v$。那么现在无非就是多了一种从 $v$ 转移的情况,也就是说,$dis_{1 u} gets min(dis_{1 u}, dis_{1, v} + 1)$。

    如果原来 $dis_{1 u} le dis_{1, v} + 1$,那么这个转移就没意义了,所以只考虑有意义的情况。显然,$dis_{2 u}$ 是不变的,所以,新的最短路长度就是

    $$ dis_{1 v} + 1 + dis_{2 u} $$

    算出缩短的长度:

    $Delta = (dis_{1 u} + dis_{2 u}) - (dis_{1 v} + 1 + dis_{2 u})$
    $quad = dis_{1 u} - dis_{1 v} - 1$

    想要让 $Delta$ 尽可能小,显然,我们选择的 $v$ 应当是满足 $dis_{1 u} - dis_{1 v}$ 最小的。而别忘了这里的条件是 $dis_{1 v} + 1 le dis_{1 u}$,也就是说,$dis_{1 v} < dis_{1 u}$。所以按照 $dis_1$ 对 $a$ 排序,边就一定是建在 $a_{i -1}$ 和 $a_i$ 之间的啦!

    有人会问,$dis_{1 a_i} = dis_{1 a_{i -1}}$,不就不满足了吗?

    没错,但是答案只和 $dis_1$ 的值有关,和 $a_i$ 无关,即便这里出现了一个无意义的建边,但是 $a_{i-1}$ 和它前面建的边,不就相当于 $a_i$ 和 $a_{i-1}$ 前面建的边吗?

    时间复杂度 $mathcal O(n log n)$。

    #include <bits/stdc++.h>
    #define rep(i, x, y) for(register int i = x; i < y; i++)
    #define REP(i, x, y) for(register int i = x; i <= y; i++)
    using namespace std;
    const int N = 2e5 + 5;
    int n, m, k, ans;
    int a[N], dis1[N], dis2[N];
    vector<int> G[N];
    queue<int> que;
    inline bool cmp(int x, int y) { return dis1[x] < dis1[y]; }
    int main()
    {
        ios::sync_with_stdio(false);
        cin >> n >> m >> k;
        REP(i, 1, k) cin >> a[i];
        REP(i, 1, m)
        {
            int u, v;
            cin >> u >> v;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        memset(dis1, -1, sizeof dis1);
        dis1[1] = 0;
        que.push(1);
        while(!que.empty())
        {
            int u = que.front();
            que.pop();
            for(int v : G[u]) if(dis1[v] == -1)
            {
                dis1[v] = dis1[u] + 1;
                que.push(v);
            }
        }
        memset(dis2, -1, sizeof dis2);
        dis2[n] = 0;
        que.push(n);
        while(!que.empty())
        {
            int u = que.front();
            que.pop();
            for(int v : G[u]) if(dis2[v] == -1)
            {
                dis2[v] = dis2[u] + 1;
                que.push(v);
            }
        }
        sort(a + 1, a + k + 1, cmp);
        ans = 0;
        rep(i, 1, k) ans = max(ans, min(dis1[n], min(dis1[a[i]] + dis2[a[i + 1]], dis1[a[i + 1]] + dis2[a[i]]) + 1));
        cout << ans << endl;
        return 0;
    }

    Solution 2

    比如我们的边添加在 $u, v$ 之间。

    一条 必须经过 $left< u, v ight>$ 的 $1 o n$ 路径长度怎么表示(不经过那么这条边就没意义了)?

    $$ min(dis_{1 u} + dis_{2 v} + 1, dis_{1 v} + dis_{2 u} + 1)$$

    原来最短路的长度显然是 $dis_{1 n}$,然后分类讨论一下。

    1. 如果 $dis_{1 u} + dis_{2 v} + 1 le dis_{1 v} + dis_{2 u} + 1$

    这时,新路径的长度为 $dis_{1 u} + dis_{2 v} + 1$。

    路径的缩短长度 $Delta = dis_{1 n} - (dis_{1 u} + dis_{2 v} + 1)$,我们想要 $Delta$ 尽可能小。

    $Delta = dis_{1 n} - dis_{1 u} - dis_{2 v} - 1$
    $quad = dis_{1 n} - 1 - (dis_{1 u} + dis_{2 v})$

    显然,$(dis_{1 u} + dis_{2 v})$ 取到 $max$ 时,$Delta$ 最小。

    2. 如果 $dis_{1 u} + dis_{2 v} + 1 ge dis_{1 v} + dis_{2 u} + 1$

    这时,新路径的长度为 $dis_{1 v} + dis_{2 u} + 1$。

    路径的缩短长度 $Delta = dis_{1 n} - (dis_{1 v} + dis_{2 u} + 1)$,我们想要 $Delta$ 尽可能小。

    $Delta = dis_{1 n} - dis_{1 v} - dis_{2 u} - 1$
    $quad = dis_{1 n} - 1 - (dis_{1 v} + dis_{2 u})$

    显然,$(dis_{1 v} + dis_{2 u})$ 取到 $max$ 时,$Delta$ 最小。

    贪心吗?注意,我们虽然是想要一个东西取到 $max$,但是这是两种情况,我们满足了一种情况的 $max$ 时很可能会发现它的条件并不满足这种情况。

    所以,排序一遍直接输出的想法告吹了 QwQ。

    那么只好在满足条件的范围内取值。

    第一种情况的条件移项后其实是什么?$dis_{1 u} - dis_{2 u} le dis_{1 v} - dis_{2 v}$。

    第二种情况呢?$dis_{1 u} - dis_{2 u} > dis_{1 v} - dis_{2 v}$。

    所以,我们只要把 $a$ 按照 $dis_1 - dis_2$ 升序排序,枚举一个 $a_i$ 作为 $v$,它前面的都是第一种情况,后面的都是第一种情况。所以,维护 前缀 $dis_1 max$ 作为 $u$,和 后缀 $dis_2 max$ 作为 $u$,都试一下就行了!

    时间复杂度也是 $mathcal O(n log n)$。

    #include <bits/stdc++.h>
    #define rep(i, x, y) for(register int i = x; i < y; i++)
    #define REP(i, x, y) for(register int i = x; i <= y; i++)
    #define PER(i, x, y) for(register int i = x; i >= y; i--)
    using namespace std;
    const int N = 2e5 + 5;
    int n, m, k, ans;
    int a[N], dis1[N], dis2[N], pre[N], suf[N];
    vector<int> G[N];
    queue<int> que;
    inline bool cmp(int x, int y) 
    {
        return dis1[x] - dis2[x] < dis1[y] - dis2[y]; 
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin >> n >> m >> k;
        REP(i, 1, k) cin >> a[i];
        REP(i, 1, m)
        {
            int u, v;
            cin >> u >> v;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        memset(dis1, -1, sizeof dis1);
        dis1[1] = 0;
        que.push(1);
        while(!que.empty())
        {
            int u = que.front();
            que.pop();
            for(int v : G[u]) if(dis1[v] == -1)
            {
                dis1[v] = dis1[u] + 1;
                que.push(v);
            }
        }
        memset(dis2, -1, sizeof dis2);
        dis2[n] = 0;
        que.push(n);
        while(!que.empty())
        {
            int u = que.front();
            que.pop();
            for(int v : G[u]) if(dis2[v] == -1)
            {
                dis2[v] = dis2[u] + 1;
                que.push(v);
            }
        }
        sort(a + 1, a + k + 1, cmp);
        REP(i, 1, k) pre[i] = max(pre[i - 1], dis1[a[i]]);
        PER(i, k, 1) suf[i] = max(suf[i + 1], dis2[a[i]]);
        REP(i, 1, k)
        {
            if(i > 1) ans = max(ans, min(dis1[n], dis2[a[i]] + 1 + pre[i - 1]));
            if(i < k) ans = max(ans, min(dis1[n], dis1[a[i]] + 1 + suf[i + 1]));
        }
        cout << ans << endl;
        return 0;
    }
  • 相关阅读:
    [整理] jQuery插件开发
    windows2008r2安装笔记
    javascript 中 typeof 和 instanceof 的区别
    mysql 复制表结构和表数据
    一个例子明白 javascript 中 for 与 for in 的区别
    实现自己的框架
    Jenkins 前端项目构建报错:Vue packages version mismatch
    linux Auditd 审计工具安装报错
    linux定时压缩日志文件脚本
    Robot Framework 3.1.2 执行测试用例报错
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1307D.html
Copyright © 2011-2022 走看看