其他分享
首页 > 其他分享> > 2021CCPC网络赛I Public Transport System

2021CCPC网络赛I Public Transport System

作者:互联网

2021CCPC网络赛I Public Transport System

题目链接

  题目中每条边边权上存在两个值,考虑将其变成一个值后进行单源最短路算法。
  不难看出,一条边的边权有取决于上条边的边权,可以想到将所有可能出现的边权与上条边权情况全部表达出来。那么对于一个入度为\(u\),出度为\(v\)的点,此点将会被拆为\(u\)个不同节点,每个节点上将有\(v\)个出度。出现了\(u\times v\)条边,最终复杂度将达到\(O(m^2logm)\)无法接受。
  考虑优化此过程,朴素的想法为通过添加若干个点,使得优惠与不优惠情况完全分隔开,但显然是无法直接添加的——由于前一个边权大小不同,所以优惠的分界线也是不同的。
  但优惠具有单调性——若有\(a_i<a_j<a_k\)且\(a_i>a_{now}\),那么\(a_i,a_j,a_k\)均能享受到优惠。添边时也只需要添加出\(a_{now}->a_i\)的优惠边即可。大于\(a_i\)的所达位置可以添加边权为\(0\)的边。
  所以在读入所有边后,按边权对其\(a\)排序,考虑\(u->v\)的一条边边权为\(a_i\),那么遍历所有\(v\)的出边,找到第一个边\(pos\)使得\(a_{pos}>a_i\),同时连接\(u\)和新节点。具体可以通过代码细化:

#include <iostream>
#include <string.h>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <map>
#include <unordered_map>
#define Lint long long
#define pi acos(-1.0)
using namespace std;
const int N = 2e5 + 10;
int T, n, m, tot;
struct node
{
    Lint a, b;
    int y;
};
vector<node> edge[N];
bool cmp(node x, node y)
{
    return x.a < y.a;
}
struct addnode
{
    Lint a, b;
    int y, id;
};
vector<addnode> sort_edge[N];
struct work
{
    struct Node
    {
        int x;
        Lint z;
        bool operator<(const Node &a) const
        {
            return z > a.z;
        }
    };
    priority_queue<Node> q;
    vector<Node> v[N * 2 + 10];
    Lint dis[N * 2 + 10];
    int vis[N * 2 + 10];
    void init(int x)
    {
        for (int i = 1; i <= x; ++i)
        {
            v[i].clear();
            dis[i] = 1e15;
            vis[i] = 0;
        }
    }
    void add(int x, int y, Lint z)
    {
        v[x].push_back({y, z});
    }
    void debug(int x){
        for(int i=1;i<=x;++i){
            for(auto j:v[i]){
                cout<<i<<" "<<j.x<<" "<<j.z<<"\n";
            }
            puts("*******************");
        }
    }
    void Dijkstra()
    {
        dis[1] = 0;
        q.push({1, dis[1]});
        while (q.size())
        {
            auto u = q.top();
            q.pop();
            if (vis[u.x])
            {
                continue;
            }
            vis[u.x] = 1;
            for (auto i : v[u.x])
            {
                if (dis[i.x] > dis[u.x] + i.z)
                {
                    dis[i.x] = dis[u.x] + i.z;
                    q.push({i.x, dis[i.x]});
                }
            }
        }
    }
    void print()
    {
        for (int i = 1; i <= n; ++i)
        {
            if (dis[i] > 1e14)
            {
                cout << "-1 ";
            }
            else
                cout << dis[i] << " ";
        }
        cout << "\n";
    }
} work;
void build(int u, int v, Lint w, Lint a)
{
    int l = 0, r = sort_edge[v].size() - 1, pos = -1;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (sort_edge[v][mid].a > a)
        {
            pos = mid;
            r = mid - 1;
        }
        else
            l = mid + 1;
    }
    if (pos == -1)
    {
        work.add(u, v, w);
    }
    else
        work.add(u, sort_edge[v][pos].id, w);
}
void solve()
{
    int x, y;
    Lint a, b;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
    {
        edge[i].clear();
        sort_edge[i].clear();
    }
    for (int i = 1; i <= m; ++i)
    {
        cin >> x >> y >> a >> b;
        edge[x].push_back({a, b, y});
    }
    for (int i = 1; i <= n; ++i)
        sort(edge[i].begin(), edge[i].end(), cmp);
    tot = n;
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 0; j < edge[i].size(); ++j)
        {
            sort_edge[i].push_back({edge[i][j].a, edge[i][j].b, edge[i][j].y, ++tot});
        }
    }
    work.init(tot);
    for (int i = 1; i <= n; ++i)
    {
        int Len = sort_edge[i].size();
        if (Len)
        {
            work.add(sort_edge[i][Len - 1].id, i, 0);
        }
        for (int j = 0; j < Len - 1; ++j)
        {
            work.add(sort_edge[i][j].id, sort_edge[i][j + 1].id, 0);
        }
        for (int j = 0; j < Len; ++j)
        {
            build(i, edge[i][j].y, edge[i][j].a, edge[i][j].a);
        }
        for (int j = 0; j < Len; ++j)
        {
            build(sort_edge[i][j].id, sort_edge[i][j].y, sort_edge[i][j].a - sort_edge[i][j].b, sort_edge[i][j].a);
        }
    }
   // work.debug(tot+2);
    work.Dijkstra();
    work.print();
}
int main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    cin >> T;
    while (T--)
        solve();
    return 0;
}
sort_edge[i].push_back({edge[i][j].a, edge[i][j].b, edge[i][j].y, ++tot});
该语句中,tot为新加入的点的编号,访问此点实际为通过打折方式访问对应目标点。
        int Len = sort_edge[i].size();
        if (Len)
        {
            work.add(sort_edge[i][Len - 1].id, i, 0);
        }
        for (int j = 0; j < Len - 1; ++j)
        {
            work.add(sort_edge[i][j].id, sort_edge[i][j + 1].id, 0);
        }
若此点存在能导出的端点,那么导出端点间将连出一条边,对应了上述的贪心过程,给予了较大边权也可享受全部优惠的效果。

        for (int j = 0; j < Len; ++j)
        {
            build(i, edge[i][j].y, edge[i][j].a, edge[i][j].a);
        }
        for (int j = 0; j < Len; ++j)
        {
            build(sort_edge[i][j].id, sort_edge[i][j].y, sort_edge[i][j].a - sort_edge[i][j].b, sort_edge[i][j].a);
        }
上述二分过程,即便是可以打折,依然可以选择原价通过,只需添加一次即可,因为后续点可以通过边权为0边到达。

这道题编码困难在分清楚新加入的点的含义——通过新加入点实际上是进行了打折这一操作,而新加入点之间的连边则是为了能够传达打折这一效果。

标签:sort,int,边权,System,Len,2021CCPC,edge,include,Transport
来源: https://www.cnblogs.com/nuist-wzy/p/16478745.html