zoukankan      html  css  js  c++  java
  • 洛谷2685 [TJOI2012]桥

    [TJOI2012]桥

    题目大意:给定一无向图,求删除一条边后1到n最短路的最大值,以及方案数。

    做法:我们先从1为起点、从n为起点跑两边dij,获得每一个点到起点1、终点n的最短距离,其实距离和边权之间的关系相当于构建了由1为根的和由n为根的最短路树---所有最短路组成的树。

    不难发现,要删除一条边,并使得最短路增大,一定要删除最短路上的边。所以我们找到从1到n的一条最短路链。找的方法就是在由n为根的最短路树上从1开始向N走。

    接下来我们从这个最短路链上每一个点,在以1为根和以n为根的最短路树上进行bfs(仅向儿子bfs)。目的是找到对于每一个不在最短路链上的点x,找到1到x最短路与1到n最短路最后一个重合的点l[x],你也可以理解为在以1为根的最短路树上x与n的lca。同理还有以n为根的最短路上x和1的lca。

    接下来我们枚举每一条不在最短路链上的边u->v,不难想到,从1到N经过边u->v的最短路一定是1->l[u]->u->v->r[v]->n的这种形式。那么在1->n的最短路上,l[u]->r[v]这一段区间内任意一条边的删除,从1到n的最短路有可能变为1->l[u]->u->v->r[v]->n。由于是最短路,所以就要更新所有这种形式的最小值。并且所有不在1->N最短路链上的边都能影响一个区间,所以这就变成了一个区间最小值问题,可以用线段树维护。用线段树维护1->N最短路上的边,线段树每一个元素代表删除这条边后最短路的长度。

    最后我们扫一遍整个线段树,找最大值即可。注意如果所有的值相等(且等于最短路径),说明无论删除哪一条边都不会使得最短路径长度增加,所以删除图里任意一条边均可,tot=m。

    代码如下(没有坑,可以直接复制提交)

    声明:如果您在非ghj1222的博客看到本篇文章且未标明转载,请联系ghj1222

    强烈谴责某网站的无耻行为

    如果下面一段代码没有高亮显示,那么您在浏览该网站。

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, m, tot, res[100010];
    
    struct SegmentTree
    {
        int tree[400010];
        
        void init(int x, int l, int r)
        {
            tree[x] = 0x3f3f3f3f;
            if (l == r)
                return;
            int mid = (l + r) >> 1;
            init((x << 1), l, mid);
            init((x << 1) | 1, mid + 1, r);
        }
        
        void update(int x, int L, int R, int cl, int cr, int key)
        {
            if (cr < L || cl > R)
                return;
            if (cl >= L && cr <= R)
            {
                tree[x] = min(tree[x], key);
                return;
            }
            int mid = (cl + cr) >> 1;
            update(x << 1, L, R, cl , mid, key);
            update((x << 1) | 1, L, R, mid + 1, cr, key); 
        }
        
        void pushdown(int x, int l, int r)
        {
            if (l == r)
            {
                res[l] = tree[x];
                return;
            }
            tree[x << 1] = min(tree[x << 1], tree[x]);
            tree[(x << 1) | 1] = min(tree[(x << 1) | 1], tree[x]);
            int mid = (l + r) >> 1;
            pushdown(x << 1, l, mid);
            pushdown((x << 1) | 1, mid + 1, r); 
        }
    }segtree;
    
    struct edge
    {
        int v, w, ne;
    }a[400010];
    bool in[400010];
    
    int h[100010], dis1[100010], dis2[100010], link[100010], id[100010], l[100010], r[100010];
    bool v[100010];
    
    void add(int x, int y, int z)
    {
        static int tmp = 0;
        a[++tmp] = (edge){y, z, h[x]};
        h[x] = tmp;
    }
    
    void dij(int *dis, int src)
    {
        for (int i = 1; i <= n; i++)
        {
            dis[i] = 0x3f3f3f3f;
            v[i] = false;
        }
        dis[src] = 0;
        priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > >q;
        q.push(make_pair(0, src));
        while (!q.empty())
        {
            int x = q.top().second;
            q.pop();
            if (v[x])
                continue;
            v[x] = true;
            for (int i = h[x]; i != 0; i = a[i].ne)
            {
                if (v[a[i].v] == false && dis[x] + a[i].w < dis[a[i].v])
                {
                    dis[a[i].v] = dis[x] + a[i].w;
                    q.push(make_pair(dis[a[i].v], a[i].v));
                }
            }
        }
    }
    
    void getLink()
    {
        int x = 1;
        while (x != n)
        {
            link[++tot] = x;
            id[x] = tot;
            for (int i = h[x]; i != 0; i = a[i].ne)
            {
                if (dis2[x] == dis2[a[i].v] + a[i].w)
                {
                    in[i] = true;
                    x = a[i].v;
                    break;
                }
            }
        }
        link[++tot] = n;
        id[n] = tot;
    }
    
    void bfs(int src, int *dis, int *arr)
    {
        queue<int> q;
        q.push(link[src]);
        arr[link[src]] = src;
        while (!q.empty())
        {
            int x = q.front();
            q.pop();
            for (int i = h[x]; i != 0; i = a[i].ne)
                if (id[a[i].v] == 0 && arr[a[i].v] == 0 && dis[x] + a[i].w == dis[a[i].v])
                {
                    arr[a[i].v] = src;
                    q.push(a[i].v);
                }
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int x, y, z, i = 1; i <= m; i++)
        {
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z);
            add(y, x, z); 
        }
        dij(dis1, 1);
        dij(dis2, n);
        getLink();
        for (int i = 1; i <= tot; i++)
            bfs(i, dis1, l);
        for (int i = tot; i >= 1; i--)
            bfs(i, dis2, r);
        segtree.init(1, 1, tot);
        for (int x = 1; x <= n; x++)
            for (int i = h[x]; i != 0; i = a[i].ne)
                if (in[i] == false && l[x] > 0 && r[a[i].v] > 0 && l[x] < r[a[i].v])
                    segtree.update(1, l[x], r[a[i].v] - 1, 1, tot, dis1[x] + a[i].w + dis2[a[i].v]);
        segtree.pushdown(1, 1, tot);
        int cnt = 0, ans = 0;
        for (int i = 1; i < tot; i++)
        {
            if (res[i] > ans)
            {
                ans = res[i];
                cnt = 1;
            }
            else if (res[i] == ans)
                cnt++;
        }
        if (ans == dis1[n])
            cnt = m;
        printf("%d %d
    ", ans, cnt);
        return 0;
    }
    
    
  • 相关阅读:
    Codeforces Round #281 (Div. 2) A. Vasya and Football(模拟)
    自动生成代码工具
    导入导出维护计划
    收集错误日志方法
    C#常用控件和属性
    人民币转换
    身份证验证
    设置下拉列表项的默认值
    清除维护任务
    清除MSSQL历史记录
  • 原文地址:https://www.cnblogs.com/oier/p/9640391.html
Copyright © 2011-2022 走看看