luoguP4366 [Code+#4]最短路 最短路

 

好久没写过博客了....

本题还是挺有趣的(很水的最短路)

关键在于怎么优化这$n^2$条连边

通常,我们希望用一些边来替代一条边从而减小边集

那么,注意到异或操作可以拆分成按位运算,因此我们只需考虑$i$和每一位异或的结果连边即可

由于我们由$i$转移到$j$时,有可能中间节点$i \wedge t$是比$i$大的

因此,实际上我们应该带着$2^t$个点跑最短路,其中$2^t \geqslant n$

然后就没什么了...

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

extern inline char gc() {
    static char RR[23456], *S = RR + 23333, *T = RR + 23333;
    if(S == T) fread(RR, 1, 23333, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;
}

#define sid 300050
#define eid 8005000
#define ll long long
#define ri register int

int n, m, c, cnp;
int vis[sid], cap[sid], nxt[eid], fee[eid], node[eid];

inline void addedge(int u, int v, int w) {
    nxt[++ cnp] = cap[u]; cap[u] = cnp;
    node[cnp] = v; fee[cnp] = w;
}

ll dis[sid];
struct P { 
    int id; ll dis; 
    friend bool operator < (P a, P b)
    { return a.dis > b.dis; }
};
priority_queue <P> q;

#define cur node[i]
void dij(int s, int t) {
    memset(dis, 127, sizeof(dis));
    dis[s] = 0; q.push((P){ s, 0 });
    while(!q.empty()) {
        int id = q.top().id; ll di = q.top().dis; q.pop();
        if(vis[id]) continue; vis[id] = 1;
        for(ri i = cap[id]; i; i = nxt[i])
        if(dis[cur] > di + fee[i]) dis[cur] = di + fee[i], q.push((P){ cur, dis[cur] });
    }
    printf("%lld\n", dis[t]);
}

int main() {
    n = read(); m = read(); c = read();
    for(ri i = 1; i <= m; i ++) {
        int u = read(), v = read(), w = read();
        addedge(u, v, w);
    }
    int N = 1;
    while(N <= n) N <<= 1;
    for(ri i = 1; i <= N; i ++)
    for(ri j = 1; j <= N; j <<= 1) addedge(i, i ^ j, j * c);
    int s = read(), t = read();
    dij(s, t);
    return 0;
}

 

转载于:https://www.cnblogs.com/reverymoon/p/9649173.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值