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, 1\leqslant m \leqslant 2 \times 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\textrm{且最小})\)连边,而\(u\)内部,从\(u_i\)\(u_{i+1}\)连一条边权为\(0\)的边。

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

优化后就变成了这样:

这样拆点后总点数\(n+m\),总边数\(3m\),用dijkstra的时间复杂度\(O(m\log 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;
}
posted @ 2021-10-14 00:00  mrclr  阅读(97)  评论(0编辑  收藏  举报