zoukankan      html  css  js  c++  java
  • HDU7134 Public Transport System

    传送门


    题意:给你一张(n)个点,(m)条边的有向图,每条边有两个权值(a_i)(b_i),对于一条路径(e_1,e_2,cdots,e_k),每条边的长度是这样计算的:

    1. (e_1)的长度是(a_{e_1})
    2. 对于边(e_i(i>1)),如果(a_{e_i} > a_{e_{i-1}}),那么长度为(a_{e_i} - b_{e_i}),否则为(a_{e_i})

    求节点(1)到其他点的最短路。((2 leqslant n leqslant 10^5, 1leqslant m leqslant 2 imes 10^5,a_i > b_i)


    这个是ccpc2021网络赛重赛的1009题,比赛上拿线段树+最短路乱搞,tle了。

    感觉图论的很多题都是在考建图,建出正确的图后,跑一个比较裸的图论算法就过了。至于原因,我也不是很清楚,总之得有这么个意识吧。


    首先的一点在于,一条边两种边权不好整,就拆成两条边权分别只有(a_i)(a_i-b_i)的边。边权为(a_i)的边任何情况下都可以走,因为最优解一定比全走(a_i)的边优,而走(a_i-b_i)的边需要满足一定的条件。那现在问题在于如何建图,使其当且仅当满足条件时,才会走(a_i-b_i)的边。

    考虑拆点。一种暴力的拆点方法是将点(u)拆成(u)的出度个,对于每个拆开的点(u_i),考虑哪些入边能接下来走(a_{u_i} - b_{u_i}).那么这一定是所有满足(a_v < a_{u_i})的入边,于是将这些边连到(u_i)上。但这样连边的复杂度特别高,要想办法优化。


    观察到这个条件是有单调性的,即如果(a_v < a_{u_i}),那么对于所有(a_{u_j} geqslant a_{u_i})的边,必然也能满足要求。因此我们可以把所有点按出边边权从大到小排序,对于一条入边(a_{v}),只向(u_i(a_{u_i} > a_v extrm{且最小}))连边,而(u)内部,从(u_i)(u_{i+1})连一条边权为(0)的边。

    这里借用官方题解的图:
    原来是这样的:

    优化后就变成了这样:

    这样拆点后总点数(n+m),总边数(3m),用dijkstra的时间复杂度(O(mlog n)).


    赛后因为数组越界发生了非常奇怪的错误,debug了半天……

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 3e5 + 5;
    const int maxe = 6e5 + 5;
    In ll read()
    {
        ll ans = 0;
        char ch = getchar(), las = ' ';
        while(!isdigit(ch)) las = ch, ch = getchar();
        while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
        if(las == '-') ans = -ans;
        return ans;
    }
    In void write(ll x)
    {
        if(x < 0) x = -x, putchar('-');
        if(x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
    In void MYFILE()
    {
    #ifndef mrclr
        freopen("random.in", "r", stdin);
        freopen("ac.out", "w", stdout);
    #endif
    }
    
    int n, m;
    struct edges
    {
        int to, a, b;
        In bool operator < (const edges& oth)const
        {
            return a < oth.a || (a == oth.a && b < oth.b);
        }
    };
    vector<edges> E[maxn];
    
    int sumd[maxn];
    In int num(int x, int p) {return sumd[x - 1] + p + 1;}
    struct Edge
    {
        int nxt, to, w;
    }e[maxe];
    int head[maxn], ecnt = -1;
    In void addEdge(int x, int y, int w)
    {
        e[++ecnt] = (Edge){head[x], y, w};
        head[x] = ecnt;
    }
    
    bool in[maxn];
    ll dis[maxn];
    #define pr pair<ll, int>
    #define mp make_pair
    #define F first
    #define S second
    In void dijkstra(int s)
    {
        priority_queue<pr, vector<pr>, greater<pr> > q;
        dis[s] = 0;
        q.push(mp(dis[s], s));
        while(!q.empty())
        {
            int now = q.top().S; q.pop();
            if(in[now]) continue;
            in[now] = 1;
            forE(i, now, v)
            {
                if(dis[v] > dis[now] + e[i].w)
                {
                    dis[v] = dis[now] + e[i].w;
                    q.push(mp(dis[v], v));
                }
            }
        }
    }
    
    In void init()
    {
        ecnt = -1;
        for(int i = 1; i <= n + m; ++i)
        {
            E[i].clear();
            head[i] = -1;
            dis[i] = INF, in[i] = 0;
        }
    }
    
    int main()
    {
    //    MYFILE();
        int T = read();
        while(T--)
        {
            n = read(), m = read();
            init();
            for(int i = 1; i <= m; ++i)
            {
                int x = read(), y = read(), a = read(), b = read();
                E[x].push_back((edges){y, a, b});
            }
            for(int i = 1; i <= n; ++i)
            {
                sumd[i] = sumd[i - 1] + E[i].size() + 1;
                sort(E[i].begin(), E[i].end());
            }
            for(int i = 1; i <= n; ++i)
            {
                int siz = E[i].size();
                if(siz) addEdge(num(i, siz - 1), num(i, siz), 0);
                for(int j = 0; j < siz; ++j)
                {
                    if(j) addEdge(num(i, j - 1), num(i, j), 0);
                    int v = E[i][j].to;
                    int pos = upper_bound(E[v].begin(), E[v].end(), (edges){0, E[i][j].a, (int)1e9}) - E[v].begin();
                    addEdge(num(i, j), num(v, pos), E[i][j].a - E[i][j].b);
                    addEdge(num(i, siz), num(v, pos), E[i][j].a);
                }
            }
            dijkstra(sumd[1]);
            for(int i = 1; i <= n; ++i)
            {
                ll Min = INF;
                for(int j = 0; j <= (int)E[i].size(); ++j) Min = min(Min, dis[num(i, j)]);
                write(Min == INF ? -1 : Min), (i == n ? enter : space);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Java异常简介
    Java中的接口
    Java中的抽象类
    Java的多态
    关于this
    面向对象的继承方式详解
    1像素边框问题
    HTML5之本地存储SessionStorage
    js数组去重的4个方法
    前端模块化
  • 原文地址:https://www.cnblogs.com/mrclr/p/15404664.html
Copyright © 2011-2022 走看看