[CSU 1808(湖南省赛16)] 地铁 (拆点建图+多源多汇最短路)

CSU - 1808 (湖南省赛16)

给定一张图,每条边有一个距离和颜色
问从 1到 n的最短路为多少
其中一条路径的长度不仅要算上距离
还要算上相邻两条边颜色的差


如果对每个点记录从某个颜色转移过来的最短路肯定是不现实的
以边为点跑最短路肯定也是不现实的,这种做法与前者无本质区别
但在CSUOJ上这两种做法都能过,可能是数据比较弱
正解是拆点,一个点每连出去一条边,就拆一个点出来
然后按颜色从小到大在相邻的内点中连上边,边权为颜色差
然后其他点按原有距离为边权连边
最终就转化为多源多汇的最短路

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <string>
#include <complex>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define SQR(a) ((a)*(a))
#define PCUT puts("\n----------")

const int maxn=1e5+10;
struct Graph
{
    int ndn,edn,last[3*maxn];
    int u[5*maxn],v[5*maxn],w[5*maxn],nxt[5*maxn];
    void init(int _n){ndn=_n; edn=0; MST(last,-1);}
    void adde(int _u, int _v, int _w)
    {
        u[edn]=_u; v[edn]=_v; w[edn]=_w;
        nxt[edn]=last[_u];
        last[_u]=edn++;
    }
};
struct data
{
    int v, c, w;
    data(){}
    data(int _v,int _c,int _w):v(_v),c(_c),w(_w){}
    bool operator < (const data &rhs) const
    {
        if(c != rhs.c) return c < rhs.c;
        if(v != rhs.v) return v < rhs.v;
        if(w != rhs.w) return w < rhs.w;
        return 0;
    }
};

int N,M;
Graph G;
vector<data> in[maxn];
vector<int> id[maxn];

LL dist[3*maxn];
bool done[3*maxn];

LL dijkstra(int,int);

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    while(~scanf("%d%d", &N, &M))
    {
        for(int i=1; i<=N; i++) in[i].clear(), id[i].clear();
        for(int i=0,u,v,c,w; i<M; i++)
        {
            scanf("%d%d%d%d", &u, &v, &c, &w);
            in[u].push_back({v,c,w}); in[v].push_back({u,c,w});
        }
        for(int i=1; i<=N; i++) sort(in[i].begin(), in[i].end());
        int tot=2;
        for(int i=1; i<=N; i++) for(int j=0; j<(int)in[i].size(); j++) id[i].push_back(++tot);
        G.init(tot);
        for(int i=0; i<(int)in[1].size(); i++) G.adde(1, id[1][i], 0);
        for(int i=0; i<(int)in[N].size(); i++) G.adde(id[N][i], 2, 0);
        for(int i=1; i<=N; i++) for(int j=0; j<(int)in[i].size(); j++)
        {
            int v=in[i][j].v, c=in[i][j].c, w=in[i][j].w;
            int tid=lower_bound(in[v].begin(), in[v].end(), data(i,c,w))-in[v].begin();
            G.adde(id[v][tid], id[i][j], w);
            if(j)
            {
                G.adde(id[i][j-1], id[i][j], abs(in[i][j-1].c - in[i][j].c));
                G.adde(id[i][j], id[i][j-1], abs(in[i][j-1].c - in[i][j].c));
            }
        }
//      for(int i=0; i<G.edn; i++) printf("%d->%d %d\n", G.u[i], G.v[i], G.w[i]);

        cout << dijkstra(1,2) << "\n";
    }
    return 0;
}

struct dstr
{
    int u; LL d;
    bool operator < (const dstr &rhs) const {return d > rhs.d;}
};
LL dijkstra(int sta, int end)
{
    priority_queue<dstr> pq;
    pq.push({sta,0});
    MST(dist,0x3f);
    dist[sta]=0;

    while(pq.size())
    {
        int u=pq.top().u;
        LL d=pq.top().d;
        pq.pop();
        if(d>dist[u]) continue;
        for(int e=G.last[u],v; ~e; e=G.nxt[e])
        {
            v=G.v[e];
            if(dist[u]+G.w[e] < dist[v])
            {
                dist[v] = dist[u]+G.w[e];
                pq.push({v,dist[v]});
            }
        }
    }

    return dist[end];
}
一种未能AC的做法

以下是我自己YY的一种做法,是基于暴力的
按普通的建图跑普通的最短路,那么对于一个点
需要枚举所有其他入边,然后再转移出去
所以可以离散化一下入边,然后用数状数组维护一下需要枚举的值中最小的
这样就能log更新,log查询,
总的时间复杂度是 (kEn) 的,空间复杂度是 (n)

但是由于这样貌似只能用SPFA转移,
而数据似乎正好有卡SPFA的,所以我的做法 TLE了。不
过我本地随机生成了几组数据,和标程对拍了一下,
似乎正确性是没有问题的。

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <string>
#include <complex>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define SQR(a) ((a)*(a))
#define PCUT puts("\n----------")

const int maxn=1e5+10;
const LL INF=0x3f3f3f3f3f3f3f3f;
struct Graph
{
    int ndn,edn,last[maxn];
    int u[2*maxn], v[2*maxn], c[2*maxn], w[2*maxn], nxt[2*maxn];
    void init(int _n){ndn=_n; edn=0; MST(last,-1);}
    void adde(int _u, int _v, int _c, int _w)
    {
        u[edn]=_u; v[edn]=_v; c[edn]=_c; w[edn]=_w;
        nxt[edn]=last[_u];
        last[_u]=edn++;
    }
};
struct Dist
{
    int siz;
    LL ans;
    vector<int> c;
    vector<LL> bit[2];
    vector<LL> tmin[2];
    void clear(){ans = INF; c.clear(); bit[0].clear(); bit[1].clear();}
    void init()
    {
        sort(c.begin(), c.end());
        siz = unique(c.begin(), c.end())-c.begin();
        c.resize(siz); 
        bit[0].resize(siz+1,INF);
        bit[1].resize(siz+1,INF);
        tmin[0].resize(siz+1,INF);
        tmin[1].resize(siz+1,INF);
    }
    void add(int _n){c.push_back(_n);}
    int id(int _n){return lower_bound(c.begin(), c.end(), _n)-c.begin()+1;}
    int query(int x)
    {
        int tid=id(x);
        LL res=INF;
        for(int i=tid; i>0; i-=i&-i) res = min(res, bit[0][i]+x);
        for(int i=siz-tid+1; i>0; i-=i&-i) res = min(res, bit[1][i]-x);
        return res;
    }
    int update(int x, LL v)
    {
        int tid=id(x);
        bool ok=0;
        if(v-x<tmin[0][tid]) tmin[0][tid] = v-x, ok=1;
        if(v+x<tmin[0][siz-tid+1]) tmin[1][siz-tid+1] = v+x, ok=1;
        if(!ok) return 0;
        for(int i=tid; i<=siz; i+=i&-i) bit[0][i] = min(bit[0][i], v-x);
        for(int i=siz-tid+1; i<=siz; i+=i&-i) bit[1][i] = min(bit[1][i], v+x);
        return 1;
    }
};

int N,M;
Graph G;
Dist dist[maxn];
bool inq[maxn];

LL SPFA();

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    while(~scanf("%d%d", &N, &M))
    {
        G.init(N);
        for(int i=1; i<=N; i++) dist[i].clear();
        for(int i=0,u,v,c,w; i<M; i++)
        {
            scanf("%d%d%d%d", &u, &v, &c, &w);
            G.adde(u,v,c,w); G.adde(v,u,c,w);
            dist[u].add(c); dist[v].add(c);
        }
        for(int i=1; i<=N; i++) dist[i].init();
        cout << SPFA() << "\n";
    }
    return 0;
}

LL SPFA()
{
    CLR(inq);
    queue<int> que;
    que.push(1);
    inq[1]=1;
    dist[1].ans = 0;
    for(auto c:dist[1].c) dist[1].update(c, 0);

    while(que.size())
    {
        int u=que.front(); que.pop();
        for(int e=G.last[u],v,c,w; ~e; e=G.nxt[e])
        {
            v = G.v[e]; c = G.c[e], w = G.w[e];
            LL d = dist[u].query(c) + w;
//          printf("%d->%d %lld\n", u, v, d);
//          dist[v].update(c, d);
            if(dist[v].update(c,d) || dist[v].ans > d)
            {
                dist[v].ans = min(dist[v].ans, d);
                if(!inq[v])
                {
                    inq[v] = 1;
                    que.push(v);
                }
            }
        }
        inq[u] = 0;
    }

    return dist[N].ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值