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;
    }
    
  • 相关阅读:
    Web API 强势入门指南
    毫秒必争,前端网页性能最佳实践
    Windbg Extension NetExt 使用指南 【3】 ---- 挖掘你想要的数据 Managed Heap
    Windbg Extension NetExt 使用指南 【2】 ---- NetExt 的基本命令介绍
    Windbg Extension NetExt 使用指南 【1】 ---- NetExt 介绍
    WCF : 修复 Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service 问题
    透过WinDBG的视角看String
    Microsoft Azure Web Sites应用与实践【4】—— Microsoft Azure网站的“后门”
    企业IT管理员IE11升级指南【17】—— F12 开发者工具
    WCF : 如何将NetTcpBinding寄宿在IIS7上
  • 原文地址:https://www.cnblogs.com/mrclr/p/15404664.html
Copyright © 2011-2022 走看看